tortoise / tortoise-orm

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

Resolved reverse reference returning a QuerySet instead the ObjectType #967

Open igormorgado opened 2 years ago

igormorgado commented 2 years ago

Describe the bug

Given the model with foreignKey:

Toy --(builder)--> Person

With related name:

Person --(toys_built) --> Toy

Defined with this code:

class Toy(tortoise.models.Model):
    id = tortoise.fields.IntField(pk=True)
    name = tortoise.fields.CharField(max_length=32)
    builder = tortoise.fields.ForeignKeyField("models.Person", related_name='toys_built')

class Person(tortoise.models.Model):
    id = tortoise.fields.IntField(pk=True)
    name = tortoise.fields.CharField(max_length=32)

When resolving the given path

Person --(toys_built)--> Toy --(builder)--> ???

Returns a QuerySet instead a Person as expected.

To Reproduce

import tortoise

class Toy(tortoise.models.Model):
    id = tortoise.fields.IntField(pk=True)
    name = tortoise.fields.CharField(max_length=32)
    builder = tortoise.fields.ForeignKeyField("models.Person", related_name='toys_built')

class Person(tortoise.models.Model):
    id = tortoise.fields.IntField(pk=True)
    name = tortoise.fields.CharField(max_length=32)

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

    gepetto = await Person.create(name="Gepheto")
    pinocchio = await Toy.create(name="Pinocchio", builder=gepetto)

    gepetto_toys_built = await gepetto.toys_built.all()

    for toy in gepetto_toys_built:
        print(toy.name)

    print(f"EXPECTED PERSON {gepetto}")
    print(f"EXPECTED PERSON {pinocchio.builder}")
    for toy in gepetto_toys_built:
        print(type(toy), toy.name)
        print(f"EXPECTED PERSON {toy.builder}")

    await tortoise.Tortoise.close_connections()

if __name__ == '__main__':
    tortoise.run_async(main())

Expected behavior

Since the object returned is of type class Toy, I would expect to be able to resolve toy.builder, exactly like the instance pinocchio.builder. But for some reason it returns a querySet. As we can see, the last printf returns a QuerySet but I expected a Person, as the output shown below

Pinocchio
EXPECTED PERSON <Person>
EXPECTED PERSON <Person>
<class '__main__.Toy'> Pinocchio
EXPECTED PERSON <tortoise.queryset.QuerySet object at 0x7fab33055d00>

Additional context

Python 3.9.7 Tortoise 0.17.8

long2ice commented 2 years ago

Use select_related

igormorgado commented 2 years ago

Can you please be more specific. I have read about this keyword in QuerySet, but didn't help. Also I could not find a single example in https://tortoise-orm.readthedocs.io/en/latest/examples.html that shows how to use it.

long2ice commented 2 years ago

https://tortoise.github.io/_modules/tortoise/queryset.html#QuerySet.select_related

igormorgado commented 2 years ago

Sorry. But do not helped. Thanks.