在自定义表单中,如何验证模型字段的唯一性(即设置unique=True
)?
我知道django的ModelForm自动执行在BaseModelForm的validate_unique()
方法中调用的clean()
函数 - 因此,当使用ModelForm时,这将被正确处理(因为它在Admin中) 。
但是,我正在从头开始创建自己的表单,并想知道如何自己处理这个问题?我认为我最大的绊脚石是知道在清理数据时哪个对象附加到表单上......
一些代码:
class UserProfile(CreatedModifiedModel):
user = models.ForeignKey(User, unique=True)
display_name = models.CharField('Display Name',max_length=30,
blank=True,unique=True)
class EditUserProfileForm(forms.Form):
display_name = forms.CharField(required=False,max_length=30)
# "notifications" are created from a different model, not the UserProfile
notifications = forms.MultipleChoiceField(
label="Email Notifications",
required=False,
widget=forms.CheckboxSelectMultiple,)
def clean_display_name(self):
# how do I run my own validate_unique() on this form?
# how do I know which UserProfile object I am working with?
# more code follows, including the __init__ which sets up the notifications
答案 0 :(得分:16)
独特的验证很难完全正确,因此无论如何我建议使用ModelForm:
class EditUserProfileForm(forms.ModelForm):
# "notifications" are created from a different model, not the UserProfile
notifications = forms.MultipleChoiceField(
label="Email Notifications",
required=False,
widget=forms.CheckboxSelectMultiple,)
class Meta:
model = UserProfile
fields = ('display_name',)
从多个模型制作表单并不容易,但在这种情况下,您只需将notifications
字段添加到ModelForm中,然后照常将其从.cleaned_data
中删除:
# view
if request.method == 'POST':
form = EditUserProfileForm(request.POST, instance=user_profile)
if form.is_valid():
user_profile = form.save()
notifications = form.cleaned_data['notifications']
# Do something with notifications.
我就是这样做的,但是如果你打算自己验证独特,你总是可以这样做:
def clean_display_name(self):
display_name = self.cleaned_data['display_name']
if UserProfile.objects.filter(display_name=display_name).count() > 0:
raise ValidationError('This display name is already in use.')
return display_name
我在这里看到两个问题。首先,您可能会遇到并发问题,其中两个人提交相同的名称,两者都通过唯一检查,但之后会出现数据库错误。另一个问题是您无法编辑用户配置文件,因为您没有要从搜索中排除的ID。您必须将其存储在__init__
中,然后在清洁中使用它:
def __init__(self, *args, **kwargs):
...
if 'instance' in kwargs:
self.id = kwargs['instance'].id
...
def clean_display_name(self):
display_name = self.cleaned_data['display_name']
qs = UserProfile.objects.filter(display_name=display_name)
if self.id:
qs = qs.exclude(pk=self.id)
if qs.count() > 0:
raise ValidationError('This display name is already in use.')
return display_name
但那时你只是复制了ModelForms中的逻辑。
答案 1 :(得分:0)
工作示例!
我将电子邮件用作唯一字段
在models.py中,使用以下代码
User._meta.get_field('email')._unique = True
在forms.py中,使用以下代码
class ProfileForm(forms.ModelForm):
email = forms.EmailField(max_length=255, required=True)
def clean_email(self):
user_id = self.cleaned_data['user'].id
email = self.cleaned_data['email']
obj = User.objects.filter(email=email).exclude(id = user_id)
if obj:
raise forms.ValidationError('User with this email is already exists.Try a new email')