Django Models Part 4 (Model validation, Model Meta, Abstract Model class)
Model Field Validation
To validate a model field on application level we can create up a validator
which is a callable that take the value to be validated and raises error (ValidationError
to be precise) if the value doesn't meet the criteria or it is invalid
The ValidationError
it raises can be imported as from django.core.exceptions import ValidationError
1. Individual Field validation
We can add callable validators to our model field through validators
arguments
Let's take a simple example of a model having a field email
which requires @djangotherightway.com
emails to be valid
from django.core.exceptions import ValidationError
from django.db import models
def django_the_right_way_email_validator(email):
if not email.endswith('@djangotherightway.com'):
raise ValidationError('Email must end with @djangotherightway.com')
class Info(models.Model):
email = models.EmailField(
validators=[django_the_right_way_email_validator]
)
You can add as many validator function/callables to a field
2. Validating multiple fields together
Sometime we come up for a need to validate two or more field together or are dependent on each other, in such scenario we can use clean()
method provided by models.Model
Let's add up simple requirements on our previous model example. Let's add up phone_number
and age
to our model, and imagine a situation where phone_number
is required if age>20
from django.core.exceptions import ValidationError
from django.db import models
def django_the_right_way_email(email):
if not email.endswith('@djangotherightway.com'):
raise ValidationError('Email must end with @djangotherightway.com')
class Info(models.Model):
email = models.EmailField(
validators=[django_the_right_way_email]
)
# make phone nullable, since phone_number is
# required only if age>20
phone_number = models.CharField(
max_length=10, null=True, blank=True
)
age = models.IntegerField()
def clean(self):
if self.age > 20:
if not self.phone_number:
raise ValidationError(
'Phone number is required if age is greater than 20'
)
Running Validators
These validators we created will not run automatically when you create/update object/row. To make this validators run, we need to call full_clean()
method which under the hood runs all the validator
Note: Validators run automatically when using
ModelForm
# this code can be on your view func or ...
# ...for this example you can use django shell
# Open Django shell using: python manage.py shell
obj = Info()
obj.email = "example@djangotherightway.com"
obj.age = 21
obj.phone_number = '1234567890'
try:
obj.full_clean()
except ValidationError as e:
print(e)
else:
obj.save()
Have a look into full_clean() Django code:Github where it runs all individual field validators like
django_the_right_way_email
in our case and even runsclean()
method we implemented for validationsYou can call
full_clean()
from your modelssave()
method, so that you don't have to manually callfull_clean()
everytime while validating objects, but this might bring up another issue offull_clean()
being called two times in case usingModelForm
.
Built-in validators
Django already has a collection of validators used for general purpose that we can use openly as a function/callable validator to our Model field
or Forms
or you own data validation
These built-in validators can be imported from django.core.validators
module
Some list of commonly used built-in validators are validate_email
, validate_slug
, validate_ipv4_address
, validate_ipv6_address
, int_list_validator
, validate_image_file_extension
e.t.c
A simple example to demonstrate use of built-in validators
from django.db import models
from django.core.validators import validate_email
class Info(models.Model):
# we didn't make email=models.EmailField()
# because models.EmailField() would already contain
# validate_email validator that checks for
# valid email addresses
email = models.CharField(validators=[validate_email])
Django models Meta class
Model Meta class in Django is a way to provide some more information regarding table_name
whether model is abstract
singular or plural names
app_label
ordering
and many more....
Python metaclass is different than that of Django model meta class. Metaclass in Python are class factory where as meta class in Django is completely different which is used to provide additional information about a model to Django. Don't get confused on they sharing the same name during our
pronouncing
speaking
listening
but they are way different on writings.
Basic example of creating Meta
options class in Django
Django automatically create a database table
for every models in the format of {app_name}_{model_name}
, so lets customize this behaviour and provide it with a custom table name
from django.db import models
class Address(models.Model):
name = models.CharField(max_length=100)
class Meta:
db_table = 'address'
Now, when we create migrations file
and run our migrations through migrate
command, django will create a table named address
instead of its default {app_name}_address
Available Models Meta Options
- abstract
- if
abstract=True
this model will be a abstract model class
- if
- db_table
- if
db_table
is set to a value, Django will take this value to createdatabase table
like we saw in previous example
- if
- managed
- if
managed=True
(default behaviour) Django will create and manage database table for this model - if
managed=False
table is not created. This might be useful to integrate already existing table into Django
- if
- ordering
- default ordering when retrieving list of objects/rows
- verbose_name
- Human readable name for the singular object
- verbose_name_plural
- Human readable plural name for the object
Abstract Model Base class (abstract=True)
Abstract model base class is also a model class which is used to place common model fields
used in other models into a single model so that it can be used/inherited into other model
.
- This type of model will not be used to create and
Database Table
by themigrations framework
- Since, this type of model doesn't create database table, we cannot perform any queries on this model
- This type of models are inherited on other models to obtain features/fields from abstract model to normal model
- So, this abstract base class is useful when you want to group common logics/fields of models into a single model but do not want to create it's corresponding database table
Let's take example of simple Information
abstract base class
from django.db import models
class InfoBase(models.Model):
name = models.CharField(max_length=100)
email = models.EmailField()
class Meta:
abstract = True
Let's use this abstract class to form other normal model class
class User(InfoBase):
address = models.CharField(max_length=100)
So, the User
model will now have fields name
email
address
, where name
and email
is inherited from the InfoBase
abstract model class
Override fields in child model
Some time, we may need to override some fields from the base class so that it gets changed in our child class. let's take an example we need to change
max_length
of name inUser
, we can do it byclass User(InfoBase): name = models.CharField(max_length=50) # changed address = models.CharField(max_length=100)
Remove/delete fields in child model
Some time, there may occur a case when we need to delete/remove some fields in our child class, we can do it by simply overriding the fields with
None
valueclass User(InfoBase): name = models.CharField(max_length=50) email = None address = models.CharField(max_length=100)
So, considering this example,
User
model will only havename
andaddress
fields