Polymorphism in Python and Django

What is polymorphism?

Polymorphism is the ability of an object to take on many forms.

When is polymorphism used?

A polymorphic model is used when a single entity requires different functionality or information.

Difference of Polymorphic and Abstract Classes

Simply put, abstract instances do not exist. An abstract class is just a way of generalizing information for future children classes. Therefore, the only objects you’ll be able to relate are the children instances, never the parent abstract class.

abstract_base_model.ModelName.field: (fields.E300) Field defines a relation with model 'ModelName', which is either not installed, or is abstract.

Django Polymorphic models

The good news is that it’s actually really straightforward to use polymorphism in Django! I’ve used the library django-polymorphic

Step 1: Install the library

pip install django-polymorphic

Step 2: Update the settings.py file

INSTALLED_APPS += (
'polymorphic',
'django.contrib.contenttypes',
)

Step 3: Define your polymorphic model

from polymorphic.models import PolymorphicModel

class Block(PolymorphicModel):
code = models.CharField(max_length=20, unique=True)

Step 4: Define your polymorphic children models

class BlockOne(Block):
text = models.CharField(max_length=255)
class BlockTwo(Block):
quote = models.CharField(max_length=255)

Step 5: Relate the polymorphic model to other models

from django.db import modelsclass BlockWrapper(models.Model):
block = models.ForeignKey(
Block,
on_delete=models.SET_NULL,
null=True,
blank=False,
)

Step 6: Query the models in the view

You can check what kind of object is the block you get. So for instance:

# get all block wrappers
block_wrappers = blockwrapper_inst.blockwrapperblock_set.all()
# iterate through the block wrappers
for block_wrapper in block_wrappers:
# now we can check what kind of block it's related to
block_inst = block_wrapper.block

if isinstance(block_inst, BlockOne):
# do stuff
elif isinstance(block_inst, BlockTwo):
# do other stuff
Block.objects.instance_of(BlockOne)

Final notes

Taking this same example, how does this really work in the backend? When you’re getting a BlockOne or a BlockTwo instance, internally Django will have to perform an INNER JOIN to get all the parent’s data. So, as the documentation says, taking performance into account “While django-polymorphic makes subclassed models easy to use in Django, we still encourage to use them with caution

Resources:

django-polymorphic library

Coder, Entrepreneur, Co-founder at SlowCode