我有一个我无法理解的问题。
假设我们有以下2个型号:
from django.db import models
class OtherModel(models.Model):
number = models.PositiveIntegerField()
class MyModel(models.Model):
name = models.CharField(max_length=15)
other_obj = models.ForeignKey(OtherModel)
deleted_at = models.DateTimeField(blank=True, null=True)
和一个基于MyModel的简单ModelForm:
from django.forms import ModelForm
class MyForm(ModelForm):
class Meta:
model = MyModel
fields = ['name', 'other_obj']
通过简单的pytest方法测试:
def test_myform():
form = MyForm(data={})
assert form.is_valid() is False
这完全正常(测试不会失败),直到我向validate_unique
添加MyModel
方法:
from django.core.exceptions import ValidationError
# ...
class MyModel(models.Model):
# ...
def validate_unique(self, exclude=None):
super(MyModel, self).validate_unique(exclude=exclude)
qs = MyModel.objects.filter(other_obj=self.other_obj)
if qs.exists() and qs[0].pk != self.pk:
e = qs[0]
raise ValidationError('This OtherModel ({}) was already used already exists'.format(e.other))
这给了我以下错误消息(RelatedObjectDoesNotExist: MyModel has no other_obj
):
F
myapp/tests/test_forms.py:66 (test_myform)
def test_myform():
form = MyForm(data={})
> assert form.is_valid() is False
test_forms.py:69:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
../../venv/lib/python3.6/site-packages/django/forms/forms.py:183: in is_valid
return self.is_bound and not self.errors
../../venv/lib/python3.6/site-packages/django/forms/forms.py:175: in errors
self.full_clean()
../../venv/lib/python3.6/site-packages/django/forms/forms.py:386: in full_clean
self._post_clean()
../../venv/lib/python3.6/site-packages/django/forms/models.py:402: in _post_clean
self.validate_unique()
../../venv/lib/python3.6/site-packages/django/forms/models.py:411: in validate_unique
self.instance.validate_unique(exclude=exclude)
test_forms.py:35: in validate_unique
qs = MyModel.objects.filter(other_obj=self.other_obj)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
self = <django.db.models.fields.related_descriptors.ForwardManyToOneDescriptor object at 0x7f01b905de10>
instance = <MyModel: MyModel object>
cls = <class 'myapp.tests.test_forms.MyModel'>
def __get__(self, instance, cls=None):
"""
Get the related instance through the forward relation.
With the example above, when getting ``child.parent``:
- ``self`` is the descriptor managing the ``parent`` attribute
- ``instance`` is the ``child`` instance
- ``cls`` is the ``Child`` class (we don't need it)
"""
if instance is None:
return self
# The related instance is loaded from the database and then cached in
# the attribute defined in self.cache_name. It can also be pre-cached
# by the reverse accessor (ReverseOneToOneDescriptor).
try:
rel_obj = getattr(instance, self.cache_name)
except AttributeError:
val = self.field.get_local_related_value(instance)
if None in val:
rel_obj = None
else:
rel_obj = self.get_object(instance)
# If this is a one-to-one relation, set the reverse accessor
# cache on the related object to the current instance to avoid
# an extra SQL query if it's accessed later on.
if not self.field.remote_field.multiple:
setattr(rel_obj, self.field.remote_field.get_cache_name(), instance)
setattr(instance, self.cache_name, rel_obj)
if rel_obj is None and not self.field.null:
raise self.RelatedObjectDoesNotExist(
> "%s has no %s." % (self.field.model.__name__, self.field.name)
)
E django.db.models.fields.related_descriptors.RelatedObjectDoesNotExist: MyModel has no other_obj.
../../venv/lib/python3.6/site-packages/django/db/models/fields/related_descriptors.py:194: RelatedObjectDoesNotExist
我假设它试图访问一个不存在的对象(OtherModel)(是None),但是不应该在clean方法中验证它? 所以我认为编写自定义清理方法可以解决问题但是以下任何一种尝试都 NOT 工作:
clean
方法添加到MyModel
:class MyModel(models.Model):
# ...
def clean(self):
if not self.other_obj:
raise ValidationError("Please set an OtherModel")
# ...
即使您将if not self.other_obj
替换为if self.other_obj is None
或if not self.other_obj_id
clean_other_obj
添加到MyModel
: class MyModel(models.Model):
# ...
def clean_other_obj(self):
if not self.other_obj: # why is this not working
raise ValidationError("other_obj cant be empty!")
# ...
clean_other_obj
添加到MyForm
:class MyForm(ModelForm):
# ...
def clean_other_obj(self):
data = self.cleaned_data['other_obj']
if not data:
raise forms.ValidationError("Please fill other_obj")
return data
clean
方法添加到MyForm
并调用super
:class MyForm(ModelForm):
# ...
def clean(self):
cleaned_data = super(MyForm, self).clean()
other_obj = cleaned_data.get("other_obj")
if not other_obj:
raise forms.ValidationError("other_obj cant be empty")
奇怪的是,这两次尝试中的任何一次 DID工作:
clean
方法添加到MyForm
和 WITHOUT 调用super
:class MyForm(ModelForm):
# ...
def clean(self):
# cleaned_data = super(MyForm, self).clean() # This doesnt work!
other_obj = self.cleaned_data.get("other_obj")
if not other_obj:
raise forms.ValidationError("other_obj cant be empty")
other_obj_id
中的validate_unique
是否有效:from django.core.exceptions import ValidationError
# ...
class MyModel(models.Model):
# ...
def validate_unique(self, exclude=None):
super(MyModel, self).validate_unique(exclude=exclude)
if not self.other_obj_id: # why is this working and not other_obj
raise ValidationError("other_obj cant be empty!")
qs = MyModel.objects.filter(other_obj=self.other_obj)
if qs.exists() and qs[0].pk != self.pk:
raise ValidationError('This OtherModel ({}) was already used.'.format(qs[0].other))
注意:这仅在您检查other_obj_id
时有效,如果您选中other_obj
它仍然无法正常工作
由于这两次尝试都是相当令人不满意的解决方案,因为在一种情况下无法调用super
方法,而在另一种情况下,validate_unique
方法未被用于清理数据而不是只检查重复,我想知道为什么会这样,我做错了什么。
我知道stackoverflow上有类似的问题,但没有一个能给出正确的答案。
非常感谢答案!
包含所有非工作尝试的代码:https://pastebin.com/y10ma8EL