tortoise / tortoise/tortoise-orm

Complex prefetch does not respect .only and always creates a full model

Open
#1,044 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Python
Stars
5.6k
Forks
516
Avg merge
2d 21h
Merged PRs (30d)
9

Description

Describe the bug
I am looking for a way to only fetch the primary keys of a many to many relation.
It's possible to to pass a queryset to Prefetch which should only create a partial model.
However currently this is ignored and always a full model is created which is a bug.

To Reproduce

from tortoise import fields, run_async, Tortoise
from tortoise.fields import ManyToManyRelation, ManyToManyField
from tortoise.models import Model
from tortoise.query_utils import Prefetch


class MyModel(Model):
    id = fields.IntField(pk=True)
    name = fields.TextField()
    properties: ManyToManyRelation["Property"]


class Property(Model):
    id = fields.IntField(pk=True)
    name = fields.TextField()
    my_models: ManyToManyRelation["MyModel"] = ManyToManyField("models.MyModel", related_name='properties')


async def run():
    # Generate the schema
    await Tortoise.init({"connections": {"default_connection": {"engine": "tortoise.backends.sqlite", "credentials": {"file_path": ':memory:'},},}, "apps": {"models": {"models": ['__main__'], "default_connection": "default_connection"},},})
    await Tortoise.generate_schemas()

    m1 = await MyModel.create(id=1, name='Model1')
    p1 = await Property.create(id=1, name='Property 1')
    await m1.properties.add(p1)

    # Fetch only ids with Prefetch
    objs = await MyModel.filter(pk=1).prefetch_related(
        Prefetch('properties', queryset=Property.all().only('id')),
    )

    print(objs)
    for e in objs[0].properties:
        print(e)
        print(e.name)   # <-- this does not crash but it should since this should only be a partial model


run_async(run())

Additional context
It would be nice if fetching the pks would be possible with the normal prefetch.
e.g.:

await MyModel.get(pk=1).first().prefetch_related('properties__id'')

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the reproduction and trace Prefetch in tortoise.query_utils together with ManyToManyRelation and ManyToManyField handling. Run the SQLite example with Property.all().only('id'); done means the prefetched relation preserves the partial model instead of creating a full model, so accessing the unloaded name field behaves as expected.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
database
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.