[英]Django: 'unique_together' and 'blank=True'
我有一个看起来像这样的Django模型:
class MyModel(models.Model):
parent = models.ForeignKey(ParentModel)
name = models.CharField(blank=True, max_length=200)
... other fields ...
class Meta:
unique_together = ("name", "parent")
这按预期工作; 如果有相同name
比同一次parent
然后我得到一个错误:“为MyModel这个名称和家长已经存在”
但是,当我使用相同的parent
保存多个MyModel
但name
字段为空时,我也会收到错误,但是应该允许这样做。 所以基本上我不想在name
字段为空时得到上述错误。 这有可能吗?
首先,空白(空字符串)与null( '' != None
)不同。
其次,当你将字段留空时,通过表单使用的Django CharField将存储空字符串 。
因此,如果你的字段不是CharField,你应该只添加null=True
。 但在这种情况下,你需要做的不仅仅是这些。 你需要创建forms.CharField
子类并覆盖它的clean
方法,以便在空字符串上返回None,如下所示:
class NullCharField(forms.CharField):
def clean(self, value):
value = super(NullCharField, self).clean(value)
if value in forms.fields.EMPTY_VALUES:
return None
return value
然后在ModelForm的表单中使用它:
class MyModelForm(forms.ModelForm):
name = NullCharField(required=False, ...)
这样,如果你把它留空,它将在数据库中存储null而不是空字符串( ''
)
使用unique_together
,您告诉Django您不希望任何两个MyModel
实例具有相同的parent
和name
属性 - 即使name
是空字符串也适用。
这是使用相应数据库列上的unique
属性在数据库级别强制执行的。 因此,要对此行为进行任何例外处理,您必须避免在模型中使用unique_together
。
相反,您可以通过覆盖模型上的save
方法并在那里强制执行唯一约束来获得所需的内容。 当您尝试保存模型的实例时,您的代码可以检查是否存在具有相同parent
和name
组合的任何现有实例,并且如果存在则拒绝保存实例。 但是如果name
是空字符串,您还可以允许保存实例。 这个的基本版本可能如下所示:
class MyModel(models.Model):
...
def save(self, *args, **kwargs):
if self.name != '':
conflicting_instance = MyModel.objects.filter(parent=self.parent, \
name=self.name)
if self.id:
# This instance has already been saved. So we need to filter out
# this instance from our results.
conflicting_instance = conflicting_instance.exclude(pk=self.id)
if conflicting_instance.exists():
raise Exception('MyModel with this name and parent already exists.')
super(MyModel, self).save(*args, **kwargs)
希望有所帮助。
这个解决方案与@bigmattyh给出的解决方案非常相似,但是,我发现下面的页面描述了应该在哪里进行验证:
http://docs.djangoproject.com/en/1.3/ref/models/instances/#validating-objects
我最终使用的解决方案如下:
from django import forms
class MyModel(models.Model):
...
def clean(self):
if self.name != '':
instance_exists = MyModel.objects.filter(parent=self.parent,
name=self.name).exists()
if instance_exists:
raise forms.ValidationError('MyModel with this name and parent already exists.')
请注意,引发了ValidationError而不是一般异常。 此解决方案的好处是,在使用.is_valid()验证ModelForm时,会自动调用上面的模型.clean()方法,并将ValidationError字符串保存在.errors中,以便它可以显示在html模板中。
如果您不同意此解决方案,请与我们联系。
bigmattyh对发生的事情给出了很好的解释。 我只想添加一个可能的save
方法。
def save(self, *args, **kwargs):
if self.parent != None and MyModels.objects.filter(parent=self.parent, name=self.name).exists():
raise Exception('MyModel with this name and parent exists.')
super(MyModel, self).save(*args, **kwargs)
我认为我选择通过覆盖我的模型的干净方法来做类似的事情,它看起来像这样:
from django.core.exceptions import ValidationError
def clean(self):
if self.parent != None and MyModels.objects.filter(parent=self.parent, name=self.name).exists():
raise ValidationError('MyModel with this name and parent exists.')
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.