在提交表单时,我很难弄清楚如何为 ForeignKey 字段自动创建模型实例。这是一个说明问题的简单玩具网站:
我有两个模型,Model1 和 Model2。Model2 包含 Model1 的 ForeignKey。我希望用户能够通过专门选择要存储在 ForeignKey 中的 Model1 的实例,或者将该值留空并让 Model1 的实例自动生成来创建 Model2 的实例。
这就是我觉得该代码应该是什么样子的。我的 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)])
forms.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。
回溯:get_response 111 中的文件“/Library/Python/2.7/site-packages/django/core/handlers/base.py”。响应=回调(请求,*callback_args,**callback_kwargs)文件“/Library/Python/2.7 /site-packages/django/views/generic/base.py" 在视图 48. return self.dispatch(request, *args, **kwargs) File "/Library/Python/2.7/site-packages/django/views/ generic/base.py” 在 dispatch 69. return handler(request, *args, **kwargs) File "/Library/Python/2.7/site-packages/django/views/generic/edit.py" in post 172. return super(BaseCreateView, self).post(request, *args, **kwargs) 文件 "/Library/Python/2.7/site-packages/django/views/generic/edit.py" 在帖子 137. if form.is_valid( ): 文件“/库/Python/2.7/site-packages/django/forms/forms.py" in is_valid 124. 返回 self.is_bound 而不是 bool(self.errors) 文件 "/Library/Python/2.7/site-packages/django/forms/forms.py " 在 _get_errors 115. self.full_clean() 文件 "/Library/Python/2.7/site-packages/django/forms/forms.py" 在 full_clean 272. self._post_clean() 文件 "/Library/Python/2.7/site -packages/django/forms/models.py" in _post_clean 309. self.instance = construction_instance(self, self.instance, opts.fields, opts.exclude) 文件 "/Library/Python/2.7/site-packages/django/ forms/models.py”在construct_instance 51. f.save_form_data(instance,cleaned_data[f.name]) 文件“/Library/Python/2.7/site-packages/django/db/models/fields/返回 self.is_bound 而不是 bool(self.errors) File "/Library/Python/2.7/site-packages/django/forms/forms.py" in _get_errors 115. self.full_clean() File "/Library/Python/2.7 /site-packages/django/forms/forms.py" 在 full_clean 272. self._post_clean() 文件 "/Library/Python/2.7/site-packages/django/forms/models.py" 在 _post_clean 309. self.instance =construct_instance(self,self.instance,opts.fields,opts.exclude)文件“/Library/Python/2.7/site-packages/django/forms/models.py”在construct_instance 51. f.save_form_data(instance,cleaned_data [ f.name]) 文件“/Library/Python/2.7/site-packages/django/db/models/fields/返回 self.is_bound 而不是 bool(self.errors) File "/Library/Python/2.7/site-packages/django/forms/forms.py" in _get_errors 115. self.full_clean() File "/Library/Python/2.7 /site-packages/django/forms/forms.py" 在 full_clean 272. self._post_clean() 文件 "/Library/Python/2.7/site-packages/django/forms/models.py" 在 _post_clean 309. self.instance =construct_instance(self,self.instance,opts.fields,opts.exclude)文件“/Library/Python/2.7/site-packages/django/forms/models.py”在construct_instance 51. f.save_form_data(instance,cleaned_data [ f.name]) 文件“/Library/Python/2.7/site-packages/django/db/models/fields/py" in _get_errors 115. self.full_clean() 文件 "/Library/Python/2.7/site-packages/django/forms/forms.py" 在 full_clean 272. self._post_clean() 文件 "/Library/Python/2.7/ site-packages/django/forms/models.py" in _post_clean 309. self.instance = construction_instance(self, self.instance, opts.fields, opts.exclude) 文件 "/Library/Python/2.7/site-packages/django /forms/models.py”在construct_instance 51. f.save_form_data(instance,cleaned_data[f.name]) 文件“/Library/Python/2.7/site-packages/django/db/models/fields/py" in _get_errors 115. self.full_clean() 文件 "/Library/Python/2.7/site-packages/django/forms/forms.py" 在 full_clean 272. self._post_clean() 文件 "/Library/Python/2.7/ site-packages/django/forms/models.py" in _post_clean 309. self.instance = construction_instance(self, self.instance, opts.fields, opts.exclude) 文件 "/Library/Python/2.7/site-packages/django /forms/models.py”在construct_instance 51. f.save_form_data(instance,cleaned_data[f.name]) 文件“/Library/Python/2.7/site-packages/django/db/models/fields/7/site-packages/django/forms/models.py" in _post_clean 309. self.instance = construction_instance(self, self.instance, opts.fields, opts.exclude) 文件 "/Library/Python/2.7/site-packages /django/forms/models.py”在construct_instance 51. f.save_form_data(instance,clean_data[f.name]) 文件“/Library/Python/2.7/site-packages/django/db/models/fields/7/site-packages/django/forms/models.py" in _post_clean 309. self.instance = construction_instance(self, self.instance, opts.fields, opts.exclude) 文件 "/Library/Python/2.7/site-packages /django/forms/models.py”在construct_instance 51. f.save_form_data(instance,clean_data[f.name]) 文件“/Library/Python/2.7/site-packages/django/db/models/fields/init .py" in save_form_data 454. setattr(instance, self.name, data) 文件 "/Library/Python/2.7/site-packages/django/db/models/fields/related.py" 在set 362. (instance. _meta.object_name,self.field.name))
异常类型:ValueError at /add/ 异常值:无法分配无:“Model2.related_model1”不允许空值。
所以,发生的事情是 Django 正在捕获我的 ValidationError 并仍然创建 Model2 的实例,即使验证失败。
如果出现错误,我可以通过覆盖 _post_clean 方法来解决此问题,以便不创建 Model2 的实例。但是,这个解决方案是丑陋的。特别是,_post_clean 的行为通常非常有帮助——在更复杂的项目中,我需要 _post_clean 来运行其他原因。
我也可以允许 ForeignKey 为空,但在实践中从不将其设置为空。但是,再一次,这似乎是个坏主意。
我什至可以设置一个虚拟的 Model1,每当对尝试的新 Model1 的验证失败时,我都会使用它,但这似乎也很骇人听闻。
一般来说,我可以想出很多技巧来解决这个问题,但我不知道如何以一种相当干净、pythonic 的方式来解决这个问题。