tortoise / tortoise-orm

Familiar asyncio ORM for python, built with relations in mind
https://tortoise.github.io
Apache License 2.0
4.67k stars 391 forks source link

Nested join does not prefetch the object. #1005

Open falled10 opened 2 years ago

falled10 commented 2 years ago

Nested join does not prefetch the object to nested object

Example

class A(models.Model):
    b = fields.ForeignKeyField(B)

class B(models.Model):
    title = fields.CharField()
    c = fields.ForeignKeyField(C)

class C(models.Model):
    title = fields.CharField()

a = await A.all().select_related('b__c')

# The SQL query actually contains 2 joins, tortoise does not store the second join
print(a.b.title) # works fine
print(a.b.c.title) # raises error AttributeError: 'QuerySet' object has no attribute 'title' 
henadzit commented 4 days ago

This seems to be fixed.

The code snippet to test it:

from tortoise import Tortoise, fields, run_async
from tortoise.models import Model

class A(Model):
    b = fields.ForeignKeyField("models.B")

class B(Model):
    title = fields.CharField(max_length=255)
    c = fields.ForeignKeyField("models.C")

class C(Model):
    title = fields.CharField(max_length=255)

async def main():
    await Tortoise.init(  # type: ignore
        db_url="sqlite://:memory:",
        modules={"models": ["__main__"]},
    )
    await Tortoise.generate_schemas()

    c = await C.create(title="test c")
    b = await B.create(title="test b", c=c)
    a = await A.create(b=b)

    query = A.all().select_related("b__c").first()
    print(query.sql())
    a = await query
    print(a.b.title)  # works fine
    print(a.b.c.title)  # raises error AttributeError: 'QuerySet' object has no attribute 'title'

run_async(main())

the output:

SELECT "a"."b_id","a"."id","a__b"."c_id" "a__b.c_id","a__b"."title" "a__b.title","a__b"."id" "a__b.id","a__b__c"."title" "a__b__c.title","a__b__c"."id" "a__b__c.id" FROM "a" LEFT OUTER JOIN "b" "a__b" ON "a__b"."id"="a"."b_id" LEFT OUTER JOIN "c" "a__b__c" ON "a__b__c"."id"="a__b"."c_id" LIMIT 1
test b
test c