I was recently faced with the challenge of needing to add translations to a hierarchal database setup in Django/Python. We wanted these translations to be stored in the database for multiple languages (for access through a CMS), where not every field on the model needed to be translated. These translations should be auto generated from when a database values changes.
Here is how I solved this with an abstract base class on a Django database model.
The original database models were defined as something like this:
import uuid
from django.db import models
class School(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
name = models.CharField(max_length=255)
description = models.CharField()
class Professor(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
name = models.CharField(max_length=255)
profile = models.CharField()
school = models.ForeignKey("app_name.School", on_delete=models.PROTECT, related_name="professors")
We want to translate both the school’s name and description, as well as the professor’s profile.
Let’s add the definition for our Translatable base class first. Note that since we use abstract = True here, and are not defining new fields, no Django database migration will be created. We simply want to tap into the save() method for each inheriting class in order to auto generate rows in another table.
from django.db import models
class Translatable(models.Model):
translatable_fields = []
def save(self, *args, **kwargs):
super().save(*args, **kwargs)
# Loop through any translatable fields and insert translations
for translatable_field in self.translatable_fields:
# Use reflection to get the actual value we just saved
translatable_field_value = getattr(self, translatable_field.attname)
# ... do other things with this value
class Meta:
abstract = True
Now we can inherit from this Translatable model in both our School and Professor models like this:
import uuid
from django.db import models
from app_name.Translatable import Translatable
class School(models.Model, Translatable):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
name = models.CharField(max_length=255)
description = models.CharField()
translatable_fields = [name, description]
class Professor(models.Model, Translatable):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
name = models.CharField(max_length=255)
profile = models.CharField()
school = models.ForeignKey("app_name.School", on_delete=models.PROTECT, related_name="professors")
translatable_fields = [profile]
This then enables us to tap into Django’s save() function to do whatever we want to do. We will get real-time values on saving.
For example, if we are saving a School object with values:
School(
name="Jasper University",
description="An example",
)
When Django’s save() method is fired, we’ll get both translatable_field_value = "Jasper University" and translatable_field_value = "An Example" as values inside of our abstract base classes loop. I then store these in another database table to be picked up in a separate process.