我有一个带有用户名和电子邮件字段的Django表单。我想查看用户尚未使用的电子邮件:
def clean_email(self):
email = self.cleaned_data["email"]
if User.objects.filter(email=email).count() != 0:
raise forms.ValidationError(_("Email not available."))
return email
这样可行,但会引发一些误报,因为电子邮件可能已经存在于表单中指定用户的数据库中。我想改为:
def clean_email(self):
email = self.cleaned_data["email"]
username = self.cleaned_data["username"]
if User.objects.filter(email=email, username__ne=username).count() != 0:
raise forms.ValidationError(_("Email not available."))
return email
Django文档说,一个字段的所有验证都是在进入下一个字段之前完成的。如果在用户名之前清除了电子邮件,则cleaned_data["username"]
中将无法使用clean_email
。但是文档还不清楚字段清理的顺序。我在表单中的电子邮件之前声明用户名,这是否意味着我可以安全地假设用户名在电子邮件之前被清除了?
我可以阅读代码,但我对Django API的承诺更感兴趣,并且知道即使在未来版本的Django中我也很安全。
答案 0 :(得分:8)
.keyOrder
不再有效。我相信这应该有效:
from collections import OrderedDict
class MyForm(forms.ModelForm):
…
def __init__(self, *args, **kwargs):
super(MyForm, self).__init__(*args, **kwargs)
field_order = ['has_custom_name', 'name']
reordered_fields = OrderedDict()
for fld in field_order:
reordered_fields[fld] = self.fields[fld]
for fld, value in self.fields.items():
if fld not in reordered_fields:
reordered_fields[fld] = value
self.fields = reordered_fields
无论您如何在表单定义中声明表单顺序,都有可能改变表单顺序的内容。其中之一就是如果您使用的是ModelForm
,在这种情况下,除非您在fields
class Meta
下宣布这两个字段,否则它们将会处于不可预测的顺序。
幸运的是,有一个可靠的解决方案。
self.fields.keyOrder
。以下是您可以使用的示例代码:
class MyForm(forms.ModelForm):
has_custom_name = forms.BooleanField(label="Should it have a custom name?")
name = forms.CharField(required=False, label="Custom name")
class Meta:
model = Widget
fields = ['name', 'description', 'stretchiness', 'egginess']
def __init__(self, *args, **kwargs):
super(MyForm, self).__init__(*args, **kwargs)
ordered_fields = ['has_custom_name', 'name']
self.fields.keyOrder = ordered_fields + [k for k in self.fields.keys() if k not in ordered_fields]
def clean_name(self):
data = self.cleaned_data
if data.get('has_custom_name') and not data.get('name'):
raise forms.ValidationError("You must enter a custom name.")
return data.get('name')
设置keyOrder
后,has_custom_name
将在验证self.cleaned_data
之前进行验证(因此会出现在name
中)。
答案 1 :(得分:7)
没有承诺以任何特定顺序处理字段。官方建议,任何依赖于多个字段的验证都应使用clean()
方法表单,而不是字段特定的clean_foo()
方法。
答案 2 :(得分:7)
Django文档声称它是按字段定义的顺序。
但我发现它并不总是能够实现这一承诺。 资料来源:http://docs.djangoproject.com/en/dev/ref/forms/validation/
这些方法按顺序运行 如上所述,一次一个场。那 是,对于表格中的每个字段(在 命令它们在表格中声明 定义),Field.clean()方法 然后运行(或其覆盖) 清洁_()。最后一次 这两种方法都是为每一种运行的 字段,Form.clean()方法或其 覆盖,执行。
答案 3 :(得分:3)
Form子类的clean()方法。这种方法可以执行任何 需要从表单访问多个字段的验证 一旦。如果字段A,您可以在此处检查该项 如果提供,则字段B必须包含有效的电子邮件地址等。 此方法返回的数据是最终的cleaning_data属性 对于表单,所以不要忘记返回已清理数据的完整列表 如果重写此方法(默认情况下,Form.clean()只返回 self.cleaned_data)。
从https://docs.djangoproject.com/en/dev/ref/forms/validation/#using-validators
复制粘贴这意味着如果你想检查电子邮件的值和parent_email不相同的东西,你应该在该函数内部进行检查。即:
from django import forms
from myapp.models import User
class UserForm(forms.ModelForm):
parent_email = forms.EmailField(required = True)
class Meta:
model = User
fields = ('email',)
def clean_email(self):
# Do whatever validation you want to apply to this field.
email = self.cleaned_data['email']
#... validate and raise a forms.ValidationError Exception if there is any error
return email
def clean_parent_email(self):
# Do the all the validations and operations that you want to apply to the
# the parent email. i.e: Check that the parent email has not been used
# by another user before.
parent_email = self.cleaned_data['parent_email']
if User.objects.filter(parent_email).count() > 0:
raise forms.ValidationError('Another user is already using this parent email')
return parent_email
def clean(self):
# Here I recommend to user self.cleaned_data.get(...) to get the values
# instead of self.cleaned_data[...] because if the clean_email, or
# clean_parent_email raise and Exception this value is not going to be
# inside the self.cleaned_data dictionary.
email = self.cleaned_data.get('email', '')
parent_email = self.cleaned_data.get('parent_email', '')
if email and parent_email and email == parent_email:
raise forms.ValidationError('Email and parent email can not be the same')
return self.cleaned_data