Python Tortoise-ORM: use related model field in __str__

8k Views Asked by At

I'm developing an API service with AIOHTTP, I try to integrate some async ORM, and the first candidate is Tortoise-ORM. In a Django project I have lots of linked models with __str__ method like the following:

from tortoise.models import Model
from tortoise import fields

class Department(Model):
    id = fields.IntField(pk=True)
    title = fields.TextField()
    upper = fields.ForeignKeyField('models.Department', related_name='children')

    def __str__(self):
        if self.upper is not None:
            return f'{self.id} Department {self.title} of {self.upper.title}'
        else:
            return f'{self.id} Department {self.title}, head'

class Employee(Model):
    id = fields.IntField(pk=True)
    name = fields.TextField()
    dep = fields.ForeignKeyField('models.Department', related_name='employees')

    def __str__(self):
        return f'{self.id}. Employee {self.name} of {self.dep.title}'

So that each object shows it's related models in description. But in Tortoise I get an error:

AttributeError: 'QuerySet' object has no attribute 'title'

I guess it's not possible to await the query in the __str__ method. So, is it possible at all use a field of related model to create object representation using Tortoise-ORM?

1

There are 1 best solutions below

0
On BEST ANSWER

Tortoise-ORM does not automatically retrieve related data for the model object from the database, until one asks it to do that. It just generates QuerySet object for related data without actually hitting the database.

To actually get data you need to use prefetch_related() or fetch_related() before printing of your objects. Documentation says:

General rule about how prefetch_related() works is that each level of depth of related models produces 1 additional query, so .prefetch_related('events__participants') will produce two additional queries to fetch your data.

Like so:

    emp_one = await Employee.filter(name="Emp_1").prefetch_related("dep").first()
    print(emp_one)

    emp_two = await Employee.filter(name="Emp_2").first()
    await emp_two.fetch_related("dep")
    print(emp_two)