tortoise-orm icon indicating copy to clipboard operation
tortoise-orm copied to clipboard

"OperationalError: ON clause references tables to its right" on self-refencing join

Open teschmitt opened this issue 3 years ago • 0 comments

I don't know if this is a bug or an error on my part: I'm trying to construct a follower-followee relationship between two user-profiles. A profile has a one-to-one relation to a user. Here is a minimal example:

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

class User(Model):
    id = fields.UUIDField(pk=True)
    username = fields.CharField(max_length=20, unique=True)

class Profile(Model):
    id = fields.IntField(pk=True)
    name = fields.CharField(max_length=255)
    following: fields.ManyToManyRelation["Profile"] = fields.ManyToManyField(
        "models.Profile", related_name="followers", through="followtable",
        backward_key="follower", forward_key="followee"
    )
    followers: fields.ManyToManyRelation["Profile"]
    owner: fields.OneToOneRelation[User] = fields.OneToOneField(
        model_name="models.User", related_name="profile"
    )

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

    # Set up follower identity: Frank Drebin
    user_drebin = await User.create(username="zucker_fan")
    profile_drebin = await Profile.create(name="Frank Drebin", owner=user_drebin)
    user_drebin_id = user_drebin.id

    # Set up followee identity: Terry Gilliam
    user_gilliam = await User.create(username="monty_fan")
    profile_gilliam = await Profile.create(name="Terry Gilliam", owner=user_gilliam)
    await profile_gilliam.followers.add(profile_drebin)
    await profile_gilliam.save()

    # Now I want to find out who Frank Drebin follows:
    # This works but involves two queries:
    p = await Profile.get(owner_id=user_drebin_id)
    who_drebin_follows = await p.following
    print(who_drebin_follows)

    # This doesn't
    print(await Profile.filter(followers__owner_id = user_drebin_id))

if __name__ == "__main__":
    run_async(run())

The second query should go through the related profiles of the followers and retrieve the ones with the given user id, but it just gets the error:

tortoise.exceptions.OperationalError: ON clause references tables to its right

This is the generated SQL:

SELECT "profile"."name","profile"."owner_id","profile"."id" FROM "profile"
    LEFT OUTER JOIN "followtable" ON "profile2"."id"="followtable"."followee"
    LEFT OUTER JOIN "profile" "profile2" ON "followtable"."follower"="profile2"."id"
WHERE "profile2"."owner_id"='dad3f7d3-095c-41c8-868f-545d4947cf7a';

But I'm looking for a way to kind of turn that statement inside out and fetch the profile2 part like this:

SELECT profile2.* FROM profile
    JOIN followtable ON profile.id = followtable.follower
    JOIN profile profile2 ON profile2.id=followtable.followee
WHERE profile.owner_id = 'dad3f7d3-095c-41c8-868f-545d4947cf7a';

What exactly am I missing in the second query statement?

teschmitt avatar Feb 12 '22 21:02 teschmitt