I found out that after a field's clean method is called, and validation
fails, that field's `clean` method returns an empty/None value for that
field. In essence, it still calls the form's `clean` method after the
validation in any of the field fails.
The validation in the form's `clean` method is dependent on the values
from those fields. If those values are `None`, or empty my validation
would produce an error.
To reproduce this, you can create a form such as this.
{{{
class ActionForm(forms.ModelForm):
phone_number = forms.CharField(required=False)
code = forms.CharField(max_length=10, required=False)
name = forms.CharField(max_length=100)
class Meta:
model = Withdrawal
fields = ['name', 'code', 'phone_number']
def clean(self):
# Ensure you can't withdraw more than your balance
cleaned_data = super().clean()
phone_number = cleaned_data.get("phone_number")
code = cleaned_data.get("code")
if phone_number == '' and code == '':
# Both code and phone number fields are empty: Raise
Validation error
raise forms.ValidationError("You must enter either the Phone
number or the code.")
user = None
if phone_number == '' or phone_number is None:
# Use user code to get user object
user = User.objects.get(code=code)
else:
# Use user phone number to get user object
user = User.objects.get(phone_number=phone_number)
def clean_phone_number(self):
# Check if User with phone number exists
phone_number = self.cleaned_data.get('phone_number')
if phone_number == '':
# If User phone number is empty, don't validate it
return phone_number
user = User.objects.filter(phone_number=phone_number)
if not user.exists():
raise forms.ValidationError('User with this Phone Number does
not exist.')
return phone_number
def clean_code(self):
# Check if user with user code exists
code = self.cleaned_data.get('code')
if code == '':
# If User code is empty, don't validate it
return code
user = User.objects.filter(code=code)
if not user.exists():
raise forms.ValidationError('User with this user code does not
exist.')
return code
}}}
In the above code, if a code or phone number is entered that is not in the
database, the lines `user = User.objects.get(code=code)` and `user =
User.objects.get(phone_number=phone_number)` would generate the following
error:
`accounts.models.User.DoesNotExist: User matching query does not exist.`
Furthermore, I printed out the value of the code and phone_number inside
the form's `clean()` method. I found out that when invalid values are
entered, it prints out empty strings or None. Hence my conclusion that
Django calls the form's `clean()` method even after validation of a
specific field fails.
--
Ticket URL: <https://code.djangoproject.com/ticket/33142>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.
* status: new => closed
* resolution: => invalid
Comment:
As far as I can tell, you've described behavior that's documented earlier
on the page you linked to: "These methods are run in the order given
above, one field at a time. That is, for each field in the form (in the
order they are declared in the form definition), the `Field.clean()`
method (or its override) is run, then `clean_<fieldname>()`. Finally, once
those two methods are run for every field, the `Form.clean()` method, or
its override, is executed whether or not the previous methods have raised
errors."
--
Ticket URL: <https://code.djangoproject.com/ticket/33142#comment:1>