tortoise / tortoise/tortoise-orm

Incorrect filtering using expressions:Q

Open
#1,432 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

Description
I am experiencing an issue with Tortoise ORM while trying to filter users (User) based on related Performer and Project objects. The code is not working correctly and is not returning the expected results.

To Reproduce

from tortoise import Tortoise, fields
from tortoise.models import Model
from tortoise.expressions import Q

# User, Project, and Performer model definitions

class User(Model):
    """User model"""

    id = fields.IntField(pk=True)
    username = fields.CharField(max_length=32, unique=True, index=True)
    creator: fields.ForeignKeyRelation["User"] = fields.ForeignKeyField(
        "models.User", null=True, on_delete=fields.SET_NULL, related_name="children"
    )


class Project(Model):
    id = fields.IntField(pk=True)
    creator: fields.ForeignKeyRelation[User] = fields.ForeignKeyField(
        "models.User", related_name="projects"
    )


class Performer(Model):
    user: fields.ForeignKeyRelation[User] = fields.ForeignKeyField(
        "models.User", related_name="performers"
    )
    project: fields.ForeignKeyRelation[Project] = fields.ForeignKeyField(
        "models.Project", related_name="performers"
    )

# Initializing Tortoise and creating the schema

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

# Creating users, projects, and their relationships

user1 = await User.create(username="user1")
project = await Project.create(creator=user1)

user2 = await User.create(username="user2", creator=user1)
await User.create(username="user3", creator=user1)
await Performer.create(user=user2, project=project)

# Executing queries and getting results

performers = Q(performers__project=project)
res1 = await project.creator.children.filter(performers)
# Expected result: [<User: 2>]

res2 = await project.creator.children.filter(~performers)
# Expected result: [<User: 3>]

children = Q(creator=project.creator)
res3 = await User.filter(children & ~performers)
# Expected result: [<User: 3>]

Expected behavior
Upon executing queries res1, res2, and res3, I expect to get the following results:

  • res1: [<User: 2>]
  • res2: [<User: 3>]
  • res3: [<User: 3>]

Actual Behavior
However, the current behavior of the code leads to incorrect results:

  • res1: [<User: 2>] (This is correct)
  • res2: [] (Expected: [<User: 3>])
  • res3: [] (Expected: [<User: 3>])

Versions
Tortoise ORM: 0.19.3
Python: 3.11.0

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 by running the provided SQLite reproducer with Tortoise ORM 0.19.3 and inspect the Q expressions used in the three User queries. Compare positive and negated relation filters, then verify that res2 and res3 return User 3 without changing the expected res1 result.

Written by the indexing model from the issue text.

Assessment

Tech stack
python, sqlite
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.