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.