【问题标题】:Can `clean` and `clean_fieldname` methods be used together in Django ModelForm?`clean` 和 `clean_fieldname` 方法可以在 Django ModelForm 中一起使用吗?
【发布时间】:2017-07-03 05:04:12
【问题描述】:

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

我们为username, email, phone, password 提供了单独的clean 方法。我不想在save() 上实现上述验证。我也不想在 User 模型中定义 clean 方法。 我已经为此表格编写了测试,并且它们通过了。但是如果我同时使用cleanclean_fieldname 可能会出现什么错误?使用视图时会不会成为问题?

我有 3 个问题:

  1. 我可以同时使用clean_fieldnameclean 方法吗? 表格?
  2. 我可以通过什么其他方式确保用户至少注册 电话还是电子邮件?
  3. clean()validate() 如何工作?我已经阅读了 django 文档,但我并不完全理解它。

这是我实现的代码。

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

【问题讨论】:

    标签: django validation django-forms modelform


    【解决方案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
    

    【讨论】:

    猜你喜欢
    • 2012-11-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-12-08
    • 1970-01-01
    • 2010-12-12
    • 2020-07-08
    相关资源
    最近更新 更多