Translations Model

I have an app that supports internationalization, and I designed the models as follows:

class EventType(models.Model):
    icon = models.CharField(_("Icon"), max_length=50, null=True, blank=True, help_text=_("Event type icon, can be found at https://tabler.io/icons"))
    color = models.CharField(_("Color"), max_length=50, null=True, blank=True, help_text=_("Event type color, can be found at https://getbootstrap.com/docs/4.0/utilities/colors/"))
    created_at = models.DateTimeField(_("Created at"), auto_now_add=True)
    updated_at = models.DateTimeField(_("Updated at"), auto_now=True)

    class Meta:
        verbose_name = _("Event Type")
        verbose_name_plural = _("Event Types")

    def get_translation(self):
        """Returns the translated name for the current language or a fallback"""
        language_code = get_language()
        translation = next((t for t in self.translations.filter(language__code=language_code)), None)
        return translation.name if translation else _("No name")

    def __str__(self):
        return self.get_translation()

class EventTypeTranslation(models.Model):
    event_type = models.ForeignKey(EventType, on_delete=models.CASCADE, related_name="translations", verbose_name=_("Event Type"))
    language = models.ForeignKey(Language, on_delete=models.CASCADE, verbose_name=_("Language"))
    name = models.CharField(_("Name"), max_length=100)
    description = RichTextField(_("Description"), null=True, blank=True)
    created_at = models.DateTimeField(_("Created at"), auto_now_add=True)
    updated_at = models.DateTimeField(_("Updated at"), auto_now=True)

    class Meta:
        verbose_name = _("Event Type Translation")
        verbose_name_plural = _("Event Type Translations")

    def __str__(self):
        return self.name

However, the queries in the Django admin are very slow due to the get_translation method.
Does anyone have a suggestion for how I can make the __str__ method return the translated name in a more performant way?

This is rather a common issue, it’s called the n+1 problem. It means that each row that is being displayed is issuing another query to the database, and that is done sequentially. For example:
Your admin does the query to get the objects (EventType)
For each object, you’re displaying the __str__ method that issues a query to the database.
So if the admin is showing 50 results, then you would have instead of the normal 4-5 queries, 54-55 queries being issued, and 50 of them would be individually issued. That’s a lot of network roundtrips.

You have a few ways to improve this.
You can override the ModelAdmin get_queryset method to prefetch the related EventTypeTranslation objects. What the prefetch_related will do is to get all the 50 EventTypeTranslation objects into a single query, and then you’ll “join” the results into each object.

Please post your ModelAdmin code, inside the admin.py in order for me to help you with that.
Or if you want to get your hands dirty, here are some docs that may help you.

In addition to the comments above, there’s also some information missing from what you’re provided here.

In:

What is self.translations? I don’t see a field or any other attribute with that name.

Also, why are you iterating over the queryset here? You’re either going to get the first entry from the queryset or None, and that can be done better without the overhead of an iterator.

It’s defined here @KenWhitesell, I believe that you haven’t seen it due to the line length/horizontal scroll. But it’s the FK related manager.

Ah, yes, I missed it off to the right.

Yes, there are definitely better ways to handle this than an iterator.
e.g.
translation = self.translations.filter(language__code=language_code).first()

But yes - creating a custom queryset with adding the prefetch related and performing the filtering in that queryset is the better solution.