2017-07-03 147 views
0

我正試圖通過電子郵件和電話在網站上實施註冊。用戶可以通過電話或電子郵件或兩者都註冊。如果用戶同時保留電話和電子郵件字段爲空,則會提出ValidationError,「您不能將電話和電子郵件字段留空,必須填寫至少一個字段。」在Django ModelForm中可以同時使用`clean`和`clean_fieldname`方法嗎?

我們有單獨的clean方法username, email, phone, password。我不想在save()上實施上述驗證。我不想在用戶模型中定義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 

回答

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 
+0

實際上,[驗證者文檔](https://docs.djangoproject.com/en/1.11/ref/forms/validation/)也明確地解釋了這個順序。 –

相關問題