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)
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.
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.']}