可以在Django ModelForm中一起使用`clean`和`clean_fieldname`方法吗?

时间:2017-07-03 05:04:12

标签: django validation django-forms modelform

我正在尝试在网站上使用电子邮件和电话进行注册。用户可以使用电话或电子邮件或两者注册。如果用户将电话和电子邮件字段都清空,则会引发ValidationError,并且#34;您不能将电话和电子邮件字段留空。您必须填写至少一个字段。"

clean我们有单独的username, email, phone, password方法。我不想在save()上实施上述验证。我也不想在用户模型中定义clean方法。 我已经为这个表格编写了测试,然后通过了。但是,如果我同时使用cleanclean_fieldname,可能会出现什么错误?处理视图时会出现问题吗?

我有3个问题:

  1. 我可以同时使用clean_fieldnameclean方法吗? 形成吗
  2. 以其他方式我可以确保用户至少注册 电话或电子邮件?
  3. clean()validate()如何运作?我已经阅读了django文档,但我完全不了解它。
  4. 这是我实施的代码。

    class RegisterForm(SanitizeFieldsForm, forms.ModelForm):
        email = forms.EmailField(required=False)
    
        message = _("Phone must have format: +9999999999. Upto 15 digits allowed."
                    " Do not include hyphen or blank spaces in between, at the"
                    " beginning or at the end.")
        phone = forms.RegexField(regex=r'^\+(?:[0-9]?){6,14}[0-9]$',
                                 error_messages={'invalid': message},
                                 required=False)
        password = forms.CharField(widget=forms.PasswordInput())
        MIN_LENGTH = 10
    
        class Meta:
            model = User
            fields = ['username', 'email', 'phone', 'password',
                      'full_name']
    
        class Media:
            js = ('js/sanitize.js', )
    
        def clean(self):
            super(RegisterForm, self).clean()
    
            email = self.data.get('email')
            phone = self.data.get('phone')
    
            if (not phone) and (not email):
                raise forms.ValidationError(
                    _("You cannot leave both phone and email empty."
                      " Signup with either phone or email or both."))
    
        def clean_username(self):
            username = self.data.get('username')
            check_username_case_insensitive(username)
            if username.lower() in settings.CADASTA_INVALID_ENTITY_NAMES:
                raise forms.ValidationError(
                    _("Username cannot be “add” or “new”."))
            return username
    
        def clean_password(self):
            password = self.data.get('password')
            validate_password(password)
            errors = []
    
            email = self.data.get('email')
            if email:
                email = email.split('@')
                if email[0].casefold() in password.casefold():
                    errors.append(_("Passwords cannot contain your email."))
    
            username = self.data.get('username')
            if len(username) and username.casefold() in password.casefold():
                errors.append(
                    _("The password is too similar to the username."))
    
            phone = self.data.get('phone')
            if phone:
                if phone_validator(phone):
                    phone = str(parse_phone(phone).national_number)
                    if phone in password:
                        errors.append(_("Passwords cannot contain your phone."))
    
            if errors:
                raise forms.ValidationError(errors)
    
            return password
    
        def clean_email(self):
            email = self.data.get('email')
            if email:
                if User.objects.filter(email=email).exists():
                    raise forms.ValidationError(
                        _("Another user with this email already exists"))
            return email
    
        def clean_phone(self):
            phone = self.data.get('phone')
            if phone:
                if User.objects.filter(phone=phone).exists():
                    raise forms.ValidationError(
                        _("Another user with this phone already exists"))
            return phone
    
        def save(self, *args, **kwargs):
            user = super().save(*args, **kwargs)
            user.set_password(self.cleaned_data['password'])
            user.save()
            return user
    

1 个答案:

答案 0 :(得分:1)

你可以从阅读Django代码中获得很多东西;这是一个评论很好的代码库!相关部分位于django/forms/forms.py。清理/验证表单后,它将调用full_clean。这将首先调用_clean_fields,调用字段clean并在要调用的表单上查找clean_{fieldname}方法。然后,调用表单clean

def full_clean(self):
    """
    Clean all of self.data and populate self._errors and self.cleaned_data.
    """
    self._errors = ErrorDict()
    if not self.is_bound:  # Stop further processing.
        return
    self.cleaned_data = {}
    # If the form is permitted to be empty, and none of the form data has
    # changed from the initial data, short circuit any validation.
    if self.empty_permitted and not self.has_changed():
        return

    self._clean_fields()
    self._clean_form()
    self._post_clean()

def _clean_fields(self):
    for name, field in self.fields.items():
        # value_from_datadict() gets the data from the data dictionaries.
        # Each widget type knows how to retrieve its own data, because some
        # widgets split data over several HTML fields.
        if field.disabled:
            value = self.get_initial_for_field(field, name)
        else:
            value = field.widget.value_from_datadict(self.data, self.files, self.add_prefix(name))
        try:
            if isinstance(field, FileField):
                initial = self.get_initial_for_field(field, name)
                value = field.clean(value, initial)
            else:
                value = field.clean(value)
            self.cleaned_data[name] = value
            if hasattr(self, 'clean_%s' % name):
                value = getattr(self, 'clean_%s' % name)()
                self.cleaned_data[name] = value
        except ValidationError as e:
            self.add_error(name, e)

def _clean_form(self):
    try:
        cleaned_data = self.clean()
    except ValidationError as e:
        self.add_error(None, e)
    else:
        if cleaned_data is not None:
            self.cleaned_data = cleaned_data