Skip to main content

Model Fields And Validations

So far we have only worked with two field types in relation to models: CharField and IntegerField. The complete list of built-in fields in Django (third-party packages provide others) is as follows:

  • AutoField
  • BigAutoField
  • BigIntegerField
  • BinaryField
  • BooleanField
  • CharField
  • DateField
  • DateTimeField
  • DecimalField
  • DurationField
  • EmailField
  • FileField
  • FilePathField
  • FloatField
  • ImageField
  • IntegerField
  • GenericIPAddressField
  • NullBooleanField
  • PositiveIntegerField
  • PositiveSmallIntegerField
  • SlugField
  • SmallIntegerField
  • TextField
  • TimeField
  • URLField
  • UUIDField
  • ForeignKey
  • ManyToManyField
  • OneToOneField

For a detailed explanation of each field type, see the official documentation at https://docs.djangoproject.com/en/stable/ref/models/fields/.

Each of these field types translates to a concrete data type of the database that has been configured, and might also has a set of built-in validations. For example, surely the CharField and EmailField field types both have the same data type in the underlying database. However, they have different validations. Fields of type CharField have, by default, only one validation concerning the length of the string, when the max_length argument is specified. Fields of type EmailField have an validation regarding the syntax of the string, that is, it must comply with the standard syntax of email addresses.

Some validations are performed by the database, others by Django, and others by both. For example, in most database engines, the character limit given to a CharField field via the max_length argument is ensured directly by the database. So, if we had defined our Course model like this:

class Course(models.Model):
name = models.CharField(max_length=128)
enrolled_students = models.IntegerField()

The following code executed in the Django interactive console will throw an error:

>>> from myapp.models import Course
>>> course = Course(name="x" * 129, enrolled_students=10)
>>> course.save()

In most database engines (SQLite excepted, because it does not establish any restriction regardless of the value of max_length) this will fail since we are passing a string of 129 characters, while the limit is 128. Note that the exception is thrown when invoking the save() method, which internally executes the query to the database. The same applies to other validations performed directly by the database.

On the other hand, validations ensured by Django are executed when invoking the full_clean() method of model instance. In this case, the number of characters in a field of type CharField is also validated by Django:

>>> course.full_clean()
Traceback (most recent call last):
...
django.core.exceptions.ValidationError: {'name': ['Ensure this value has at most 128 characters (it has 129).']}

It is not that common to full_clean() directly. In the next section we will see that it is automatically invoked by Django when generating a form from a model.

Let's consider another example. We have already used the choices parameter, which allowed a set of options to be configured on a form field. The good thing is that it is also available for models. So, let's add the time field to our Course model:

class Course(models.Model):
name = models.CharField(max_length=128)
enrolled_students = models.IntegerField()
TIMES_OF_DAY = (
(1, "Morning"),
(2, "Afternoon"),
(3, "Evening")
)
time = models.PositiveSmallIntegerField(choices=TIMES_OF_DAY, null=True)
note

Django provides five field types for numeric values, namely: BigIntegerField, IntegerField, PositiveIntegerField, PositiveSmallIntegerField and SmallIntegerField. In most database engines, the difference between them is whether or not they accept negative numbers (those starting with Positive do not accept) and how large the number can be (the exact range of accepted values depends of the database). The enrolled_students field could also be changed to PositiveSmallIntegerField since it will never contain negative or very large numbers.

note

Since we are adding a field to a model that might already have stored data (such as courses stored in previous chapters), we must pass null=True to allow that field to be empty for pre-existing courses. By default, all fields are required (i.e., null=False). We will see this topic in detail in the Adding Fields to Models With Pre-existing Data section.

We have just indicated that the time field can only have three options: 1, 2 or 3. "Morning", "Afternoon", and "Evening" is what the user will see instead of these numbers. However, this is a restriction enforced only by Django, not by the database. Before checking this, considering that we made changes to the model, you must create and apply a migration (otherwise your database table schema won't match your model):

python manage.py makemigrations myapp
python manage.py migrate myapp

Run the Django shell again and execute:

>>> from myapp.models import Course
>>> course = Course(name="Swift", enrolled_students=12, time=4)
>>> course.save()

Note that time=4 is not a valid option. However, save() does not throw any errors and successfully inserts the course into the database. The method that correctly validates this value is full_clean(), which performs Django's validations (as opposed to the database engine's validations):

>>> course.full_clean()
...
django.core.exceptions.ValidationError: {'time': ['Value 4 is not a valid choice.']}