tortoise / tortoise-orm

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

QuerySet.count(): IndexError: list index out of range #1590

Closed PieceOfGood closed 1 month ago

PieceOfGood commented 2 months ago

Describe the bug

To Reproduce

class BaseModel(Model):
    class Meta:
        abstract = True

    id = fields.IntField(pk=True, index=True)

class BaseProp(BaseModel):
    class Meta:
        abstract = True

    name: str = fields.CharField(50, null=False, unique=True)

class Property(BaseProp):
    class Meta:
        table = "properties"

    customs: fields.ManyToManyRelation["Custom"]

class Custom(BaseModel):
    class Meta:
        table = "customs"

    short = fields.CharField(50, null=False, unique=True)

    properties: fields.ManyToManyRelation["Property"] = fields.ManyToManyField(
        "models.Property", related_name="customs"
    )

async def make() -> None:
    custom_one = await Custom.create(short="one")
    custom_two = await Custom.create(short="two")
    custom_three = await Custom.create(short="three")
    custom_four = await Custom.create(short="four")

    fine = await Property.create(name="fine")
    light = await Property.create(name="light")
    pink = await Property.create(name="pink")

    await custom_one.properties.add(fine, light, pink)
    await fine.customs.add(custom_two, custom_three, custom_four)
    await custom_three.properties.add(light)
    await custom_four.properties.add(pink, light)

async def main() -> None:
    await Tortoise.init(config=TORTOISE_CONFIG)
    await Tortoise.generate_schemas()

    await make()
    query = (
        Custom.filter(properties__id__in=[1, 2, 3])
        .annotate(count=Count("id"))
        .filter(count=3)
        .prefetch_related("properties")
    )

    result = await query
    print(result)
    for r in result:
        print(r.short, list(r.properties))

    print(await query.count())

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

Output:

[<Custom: 1>, <Custom: 4>]
one [<Property: 1>, <Property: 2>, <Property: 3>]
four [<Property: 1>, <Property: 3>, <Property: 2>]
Traceback (most recent call last):
  File "G:\pyProj\TortoiseModels\m2m_query_trouble.py", line 116, in <module>
    run_async(main())
  File "C:\Python\Python311\Lib\site-packages\tortoise\__init__.py", line 688, in run_async
    loop.run_until_complete(coro)
  File "C:\Python\Python311\Lib\asyncio\base_events.py", line 653, in run_until_complete
    return future.result()
           ^^^^^^^^^^^^^^^
  File "G:\pyProj\TortoiseModels\m2m_query_trouble.py", line 112, in main
    print(await query.count())
          ^^^^^^^^^^^^^^^^^^^
  File "C:\Python\Python311\Lib\site-packages\tortoise\queryset.py", line 1280, in _execute
    count = list(dict(result[0]).values())[0] - self.offset
                      ~~~~~~^^^
IndexError: list index out of range

Expected behavior

[<Custom: 1>, <Custom: 4>]
one [<Property: 1>, <Property: 2>, <Property: 3>]
four [<Property: 1>, <Property: 3>, <Property: 2>]
2

Additional context Win 10 Python 3.11.5

abondar commented 2 months ago

Yeah, can confirm there is bug when propagating annotations info for non-filter queries, resulting in incorrect query generating

We'll look into fixing it

abondar commented 2 months ago

Will close when release will be ready

abondar commented 1 month ago

Fixed in 0.20.1 Although there is related problem in #1607 Will be fixed in 0.21.0