form validation doc https://docs.djangoproject.com/en/3.0/ref/forms/validation/

참고 블로그) 내용이 official doc보다 간결하다. http://www.deekras.com/django-validations-during-form-processing.html

These are normally executed when you call the is_valid() method on a form. There are other things that can also trigger cleaning and validation (accessing the errors attribute or calling full_clean() directly), but normally they won’t be needed. 

(form validation은 form의 is_valid()를 통해 수행된다. form에 있는 errors에 접근하거나 full_clean()을 통해 수행되기도 한다. full_clean()는 model validation에서 사용되는 method이름과 같다.)

  •  to_python() 스트링을 python data type으로 전환한다.
  • validate() method on a Field handles field-specific validation that is not suitable for a validator. 
  • run_validators() method on a Field runs all of the field’s validators and aggregates all the errors into a single ValidationError. You shouldn’t need to override this method.
  • clean() method on a Field subclass is responsible for running to_python(), validate(), and run_validators() in the correct order and propagating their errors. This method returns the clean data, which is then inserted into the cleaned_data dictionary of the form.
  • clean_<fieldname>() method is called on a form subclass. This method does any cleaning that is specific to that particular attribute, unrelated to the type of field that it is. This method is not passed any parameters. You will need to look up the value of the field in self.cleaned_data and remember that it will be a Python object at this point, not the original string submitted in the form (it will be in cleaned_data because the general field clean() method, above, has already cleaned the data once).

기본적으로 위와 같은 순서로 진행되며 각 단계에서 무제가 발생하면 ValidationError를 raise한다.

Raising ValidationError

# Good
ValidationError(_('Invalid value'), code='invalid')

# Bad
ValidationError(_('Invalid value'))

# Good
ValidationError(
    _('Invalid value: %(value)s'),
    params={'value': '42'},
)

# Bad
ValidationError(_('Invalid value: %s') % value)

# Good
ValidationError(
    _('Invalid value: %(value)s'),
    params={'value': '42'},
)

# Bad
ValidationError(
    _('Invalid value: %s'),
    params=('42',),
)

Wrap the message with gettext to enable translation: _() 를 말한다.

# Good
ValidationError(_('Invalid value'))

# Bad
ValidationError('Invalid value')

Putting it all together:

raise ValidationError(
    _('Invalid value: %(value)s'),
    code='invalid',
    params={'value': '42'},)

Raising multiple errors

# Good
raise ValidationError([
    ValidationError(_('Error 1'), code='error1'),
    ValidationError(_('Error 2'), code='error2'),
])

# Bad
raise ValidationError([
    _('Error 1'),
    _('Error 2'),
])

기존 field를 extends해서 새로운 field를 만들고 새로운 validation rule을 지정하는 방법

1방법)

from django.core import validators
from django.forms import CharField

class SlugField(CharField):
    default_validators = [validators.validate_slug]
slug = forms.SlugField()

2방법)

slug = forms.CharField(validators=[validators.validate_slug])

validator만드는 방법 https://docs.djangoproject.com/en/3.0/ref/validators/

form의 field에 validation을 customize하는 방법 (to_python , validate)

from django import forms
from django.core.validators import validate_email

class MultiEmailField(forms.Field):
    def to_python(self, value):
        """Normalize data to a list of strings."""
        # Return an empty list if no input was given.
        if not value:
            return []
        return value.split(',')

    def validate(self, value):
        """Check if value consists only of valid emails."""
        # Use the parent's handling of required fields, etc.
        super().validate(value)
        for email in value:
            validate_email(email)
class ContactForm(forms.Form):
    subject = forms.CharField(max_length=100)
    message = forms.CharField()
    sender = forms.EmailField()
    recipients = MultiEmailField()
    cc_myself = forms.BooleanField(required=False)

Use MultiEmailField like any other form field. When the is_valid() method is called on the form, the MultiEmailField.clean() method will be run as part of the cleaning process and it will, in turn, call the custom to_python() and validate() methods.

form 단위에서 field validation 을 customize 하는 경우

from django import forms

class ContactForm(forms.Form):
    # Everything as before.
    ...

    def clean_recipients(self):
        data = self.cleaned_data['recipients']
        if "fred@example.com" not in data:
            raise forms.ValidationError("You have forgotten about Fred!")

        # Always return a value to use as the new cleaned data, even if
        # this method didn't change it.
        return data

이미 fileld단위의 validation이 끝났기 때문에 cleaned_data에 접근 가능하다.

Cleaning and validating fields that depend on each other (form 단위에서 여러개의 fields을 이용해서 validation하는 경우)

from django import forms

class ContactForm(forms.Form):
    # Everything as before.
    ...

    def clean(self):
        cleaned_data = super().clean()
        cc_myself = cleaned_data.get("cc_myself")
        subject = cleaned_data.get("subject")

        if cc_myself and subject:
            # Only do something if both fields are valid so far.
            if "help" not in subject:
                raise forms.ValidationError(
                    "Did not send for 'help' in the subject despite "
                    "CC'ing yourself."
                )

In this code, if the validation error is raised, the form will display an error message at the top of the form (normally) describing the problem.

The call to super().clean() in the example code ensures that any validation logic in parent classes is maintained. If your form inherits another that doesn’t return a cleaned_data dictionary in its clean() method (doing so is optional), then don’t assign cleaned_data to the result of the super() call and use self.cleaned_data instead:

def clean(self):
    super().clean()
    cc_myself = self.cleaned_data.get("cc_myself")
    ...
from django import forms
class ContactForm(forms.Form):
    # Everything as before.
    ...

    def clean(self):
        cleaned_data = super().clean()
        cc_myself = cleaned_data.get("cc_myself")
        subject = cleaned_data.get("subject")

        if cc_myself and subject and "help" not in subject:
            msg = "Must put 'help' in subject when cc'ing yourself."
            self.add_error('cc_myself', msg)
            self.add_error('subject', msg)

Comments are closed.

Post Navigation