Mandemon
Mandemon

Reputation: 422

Searching one-to-one-to-many relationship

I have three models, let's call them models A, B and C.

A and B share one-to-one relationship, A can exists without B but B must always be linked to A. C has many-to-one relation with B. Or in other words:

class ModelA(models.Model):
  #properties

class ModelB(models.Model):
  a_link = models.OneToOneField(A, on_delete=models.CASCADE, related_name='a_link')
  #rest of the properties

class ModelC(models.Model):
  set = models.ForeignKey(ModelB, on_delete=models.CASCADE)
  user = models.ForeignKey(User, on_delete=models.SET_NULL, blank=True, null=True)

Now, my question is, I want to find all A's that user has, by checking what Cs they have been tagged in. Can I just do search ModelA.objects.filter(a_link__modelc__user=user) or do I need to first search for Cs in their own query, then select Bs from them and look for their relationship with A?

Upvotes: 1

Views: 333

Answers (1)

willeM_ Van Onsem
willeM_ Van Onsem

Reputation: 477308

You can filter with:

ModelA.objects.filter(a_link__modelc__user=user).distinct()

The .distinct() is useful here to avoid that the same ModelA is in the QuerySet multiple times if there are multiple related ModelCs that have as user the user object.

Since the related_name is however the name of the relation in reverse, you might want to rename this to for example:

class ModelA(models.Model):
  #properties

class ModelB(models.Model):
    a_link = models.OneToOneField(
        ModelA,
        on_delete=models.CASCADE,
        related_name='b_object'
    )
    # rest of the properties

class ModelC(models.Model):
    set = models.ForeignKey(ModelB, on_delete=models.CASCADE)
    user = models.ForeignKey(
        User,
        on_delete=models.SET_NULL,
        blank=True,
        null=True
    )

then the query is thus:

ModelA.objects.filter(b_object__modelc__user=user).distinct()

Note: It is normally better to make use of the settings.AUTH_USER_MODEL [Django-doc] to refer to the user model, than to use the User model [Django-doc] directly. For more information you can see the referencing the User model section of the documentation.

Upvotes: 1

Related Questions