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_emailin 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
pronouncingspeakinglisteningbut 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=Truethis model will be a abstract model class
- if
- db_table
- if
db_tableis set to a value, Django will take this value to createdatabase tablelike we saw in previous example
- if
- managed
- if
managed=True(default behaviour) Django will create and manage database table for this model - if
managed=Falsetable 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 Tableby 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_lengthof 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
Nonevalueclass User(InfoBase): name = models.CharField(max_length=50) email = None address = models.CharField(max_length=100)So, considering this example,
Usermodel will only havenameandaddressfields






