Understand clean() and clean_<fieldname>() in Django

Oct. 17, 2020, 9:17 a.m.

In this tutorial we are going to focus on Form.clean() method and clean_fieldname() method , these methods help us validate data entered in forms by users.


we are going to use this app https://github.com/felix13/djangoclean for us to understand better.


lets say we have an app that takes in student information , for example, first name , last name and an image of the student.


We also want to validate the data entered by students and after thinking about it, we settle on the following rules.


  • No student can have the same name as first name and last name, for example if you enter your first name as " Felix " and last name as " Felix " our app will throw an error.


  • We are going to allow students to upload images that have the following extensions only ( 'png' , 'jpg' , 'jpeg' ) and anything else will throw an error.


so if a user uploads a video with an extension of lets say .mp4 our app should reject it.


I know we have people with the same name as first name and last name, this is just for demonstration purposes.

Now lets lets dive into code and see how our app will look like, we will start with models.py file

from django.db import models

class Document(models.Model):
    first_name = models.CharField(max_length=255, blank=True)
    last_name = models.CharField(max_length=255, blank=True)
    image = models.FileField(upload_to='images/')
    uploaded_at = models.DateTimeField(auto_now_add=True)

In the above code we have a Document model which has fields for first_name, last_name, image and uploaded_at , Each attribute of the model represents a database field.

Lets look at our forms.py file , it will look like this


class DocumentForm(forms.ModelForm):
    class Meta:
        model = Document
        fields = ('first_name', 'last_name', 'image', )



    def clean_image(self):

        IMAGE_FILE_TYPES = ['png', 'jpg', 'jpeg']

        uploaded_image = self.cleaned_data.get("image",  False)

        extension = str(uploaded_image).split('.')[-1]

        file_type = extension.lower()

        if not uploaded_image:       
            raise ValidationError("please upload an Image") # handle empty image


        if file_type not in IMAGE_FILE_TYPES:
            raise ValidationError("File is not image.")

        return uploaded_image





    def clean(self):
        cleaned_data = super().clean()
        first_name = cleaned_data.get('first_name')
        last_name  = cleaned_data.get('last_name')

        if first_name == last_name:
            raise ValidationError( "First name and last name cannot be the same." )

Let me explain what is going on in our forms.py file, first of all , we have two methods which are

  • clean_image()
  • clean()

Explanation

clean_image()

You might be asking yourself , why is our method called clean_image() ? , in Django, they generally call this method clean_<fieldname>() where <fieldname> is replaced with the name of the form field attribute . In our example since we want to clean a specific field which is the image field, we will replace the <fieldname> with image making it clean_image().

Our method here does two things

  • Check if image is uploaded, if not raise validation error.
  • Check if image extension is of the type that is allowed ( 'png' , 'jpg' , 'jpeg' ) , if not raise validation error.

clean()

This is the form's clean() method , do not confuse it with clean() method on a field that we did above. Django docs says

It’s important to keep the field and form difference clear when working out where to validate things. Fields are single data points, forms are a collection of fields.

read more on this here

This method is used in cleaning and validating fields that depend on each other. In our case we are checking if first_name == last_name , if equal then we raise validation error.

Conclusion

clean_<fieldname>()

This is validation that is specific , we did write a clean method that operates on the image field.

clean()

Used in performing validation on more than one field at a time, In our example we did comparison of two fields by checking if first_name == last_name .


Source code

You can find the source code of the app used in this example here

Resources

photo by Kari Shea on unsplash

Keep Learning