Model Translations

Hi
My app have these two models.
Templates

class Templates(models.Model):
    id = models.BigAutoField(primary_key=True)
    created_at = models.DateTimeField(blank=True, null=True, auto_now_add=True)
    updated_at = models.DateTimeField(blank=True, null=True, auto_now=True)
    deleted_at = models.DateTimeField(blank=True, null=True)
    image_url = models.TextField(blank=True, null=True)
    type = models.TextField()
    model = models.TextField(blank=True, null=True)
    temperature = models.FloatField(blank=True, null=True)
    max_tokens = models.BigIntegerField(blank=True, null=True)
    top_p = models.FloatField(blank=True, null=True)
    frequency_penalty = models.FloatField(blank=True, null=True)
    presence_penalty = models.FloatField(blank=True, null=True)
    has_voice_tone = models.BooleanField(blank=True, null=True)
    has_examples = models.BooleanField(blank=True, null=True)
    has_keywords = models.BooleanField(blank=True, null=True)
    categories = models.ManyToManyField(
        'Categories', through="TemplateCategories", blank=True)

    class Meta:
        managed = False
        db_table = 'templates'

TemplateTranslations

class TemplateTranslations(models.Model):
    id = models.BigAutoField(primary_key=True)
    created_at = models.DateTimeField(blank=True, null=True, auto_now_add=True)
    updated_at = models.DateTimeField(blank=True, null=True, auto_now=True)
    deleted_at = models.DateTimeField(blank=True, null=True)
    template = models.ForeignKey('Templates', models.DO_NOTHING)
    language = models.ForeignKey(Languages, models.DO_NOTHING)
    name = models.TextField()
    description = models.TextField()
    video_url = models.TextField()

    def __str__(self):
        return self.name

    class Meta:
        managed = False
        db_table = 'template_translations'

I want get name for the each template at language=1, but when i set str the ORM django does much queries

example

def __str__(self):
        translation = self.templatetranslations_set.filter(language=1).first()
        return translation.name if translation else str(self.id)
``

Yep pretty standard related model access from a __str__() then suddenly the database is hammered :slight_smile: Sounds like you’re displaying a list of models somewhere and getting n+1 queries. I usually recommend to people avoiding doing related model lookups from __str__(), but hey sometimes practicality beats purity.

You need to make sure the queryset is appropriately setup for this. Please read: Database access optimization | Django documentation | Django and consider using prefetching (all is explained in the docs) :slight_smile:

Happy to answer further Q’s you may have, good luck!

1 Like

What you’re likely going to want to do here is annotate the translation field on to your Templates model in the query.

You can do this either in the view where you’re running the query, or if this is something that you’re always going to want to do, do it in a custom manager.

Also see Conditional Expressions | Django documentation | Django

1 Like

Thanks for the answer, but how i can use prefetch_related in my model class?

You wouldn’t. You would use it in the view where you’re making the query. (Or in a manager or custom queryset as referenced above.)

Oh i undestand, i question because i these other models and i try create a form use ModelChoiceField in field template

Models.py

class TemplateFieldTranslations(models.Model):
    id = models.BigAutoField(primary_key=True)
    created_at = models.DateTimeField(blank=True, null=True, auto_now_add=True)
    updated_at = models.DateTimeField(blank=True, null=True, auto_now=True)
    deleted_at = models.DateTimeField(blank=True, null=True)
    name = models.TextField()
    description = models.TextField()
    template_field = models.ForeignKey('TemplateFields', models.DO_NOTHING)
    language = models.ForeignKey(Languages, models.DO_NOTHING)

    def __str__(self):
        return self.name

    class Meta:
        managed = False
        db_table = 'template_field_translations'
        unique_together = (('template_field', 'language'),)


class TemplateFields(models.Model):
    id = models.BigAutoField(primary_key=True)
    created_at = models.DateTimeField(blank=True, null=True, auto_now_add=True)
    updated_at = models.DateTimeField(blank=True, null=True, auto_now=True)
    deleted_at = models.DateTimeField(blank=True, null=True)
    key = models.TextField()
    data_type = models.TextField()
    template = models.ForeignKey(Templates, models.DO_NOTHING)
    order = models.BigIntegerField(blank=True, null=True)
    use_brand_name = models.BooleanField(blank=True, null=True)

    class Meta:
        managed = False
        db_table = 'template_fields'

forms.py

class TemplateFieldsForm(forms.ModelForm):
    template = forms.ModelChoiceField(
        queryset=TemplateTranslations.objects.filter(
            language=1).select_related('template'),
        to_field_name="template_id",
        widget=forms.Select(
                attrs={
                    'class': 'select2'
                }
            )
    )

and its not pretty show id or object in select options, so i was trying to put the translate, but dont work, return this error

That error is caused by this:

The template field in the TemplateFields model is a foreign key to Templates, so your queryset must return instances of Templates, not TemplateFields.

What you’re probably going to want to do here is use the iterator attribute of the ModelChoiceField to create a custom representation of the select options.

You would still have a custom queryset - one that annotates the desired translation in the Templates model instances. You would then add the custom subclass as described in the docs to return that annotated value as the display option for that field.

1 Like

Thanks KenWhitesell! it was a great help. I used iterator and worked. I just dont understand still how can i use annotate.