2017-07-03 146 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/)也明确地解释了这个顺序。 –

相关问题