Cannot access foreign key ID in nested join

Let’s have models like this:

class A(models.Model):
    id = models.UUIDField()

class B(models.Model):
    id = models.UUIDField()
    a = models.ForeignKey(A)

class C(models.Model):
    id = models.UUIDField()
    b = models.ForeignKey(B)

I can do this:

C.objects.filter(a__b__id=<something>)

But I cannot do this:

C.objects.filter(a__b_id=<something>)

I get an error like this:

FieldError: Unsupported lookup 'b_id' for ForeignKey or join on the field not permitted.

The reason this matters is the working version produces an extra join on A:

SELECT "a"."id" FROM "c" INNER JOIN "b" ON ("c"."b_id" = "b"."id") LEFT OUTER JOIN "a" ON ("b"."id" = "a"."b_id")

It could just as well get:

SELECT "b"."a_id" FROM "c" INNER JOIN "b" ON ("c".b_id" = "b"."id")

Is it possible to make Django behave this way? If not, why not?

I believe this is a scenario that you can use FilteredRelation, i don’t use it very often to give a specific advice on it, but you can try it out.

It appears to have the same issue.

Not sure if there’s a misunderstanding here as there is no way to query A directly from C; you need to hop through B.

From this model definition against the latest version of Django

from django.db import models

class A(models.Model):
    id = models.UUIDField(primary_key=True)

class B(models.Model):
    id = models.UUIDField(primary_key=True)
    a = models.ForeignKey(A, models.CASCADE)

class C(models.Model):
    id = models.UUIDField(primary_key=True)
    b = models.ForeignKey(B, models.CASCADE)

I can do

In [1]: from app.models import C

In [2]: C.objects.filter(b__a__id=1).query.__str__()
Out[2]: 'SELECT "app_c"."id", "app_c"."b_id" FROM "app_c" INNER JOIN "app_b" ON ("app_c"."b_id" = "app_b"."id") WHERE "app_b"."a_id" = 00000000000000000000000000000001'

In [3]: C.objects.filter(b__a=1).query.__str__()
Out[3]: 'SELECT "app_c"."id", "app_c"."b_id" FROM "app_c" INNER JOIN "app_b" ON ("app_c"."b_id" = "app_b"."id") WHERE "app_b"."a_id" = 00000000000000000000000000000001'

In [4]: C.objects.filter(b__a_id=1).query.__str__()
Out[4]: 'SELECT "app_c"."id", "app_c"."b_id" FROM "app_c" INNER JOIN "app_b" ON ("app_c"."b_id" = "app_b"."id") WHERE "app_b"."a_id" = 00000000000000000000000000000001

which all make use of INNER JOIN which I tested all the way from Django 3.2.

I constructed the example from real code that I didn’t want to post to avoid complexity. Dangerously, I did not test it out.

I missed something in my models. The issue is coming from using the opposite end of a one-to-one field, which of course shouldn’t work. Big oversight on my part in posting here. I thought I had checked all that.

1 Like