Left join with User

I’ve got these models:

class User(AbstractUser):
    friends = models.ManyToManyField('self', through='Friend')
    friends_count = models.PositiveIntegerField(default=0)
    followers_count = models.PositiveIntegerField(default=0)


class Friend(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='user_friends', related_query_name='user_friend')
    friend = models.ForeignKey(User, on_delete=models.CASCADE, related_name='followers', related_query_name='follower')

    class Meta:
        constraints = [
            models.UniqueConstraint(fields=['user', 'friend'], name='unique_friend')
        ]

In a function based view I want to retrieve all the users except the logged in user:

users = models.User.objects.exclude(id=request.user.id)

In the template I want to list the users and add a follow or unfollow button depending on whether the logged in user already follows the user or not. I think I need to join the users with the friend objects where user_id is the id of the logged in user. I had a look at aggregation but wasn’t able to figure out how to get this right.

Kind regards.

Johanna

Is a “follow” the same as making an entry in Friend?
If so, then one way to do this is to get a list of friends and check to see if the users you are iterating over are in that list or not.

In other words, for a given User a_user, then the list of friends is a_user.friends.all().
If other_user is in a_user.friends.all(), then other_user is a friend of a_user. If other_user is not in that list, then no such association exists.

Hi Ken,

Thanks for your reply.

Yes, follow is the same as making an entry into friend.

I had a look at the Queryset API again and wonder whether this could be done using

Based on the pizza example, I tried:

users = models.User.objects.annotate(has_friend=FilteredRelation('friends', condition=Q(friends__user_id=request.user.id)),).filter(has_friend__isnull=True)

This raises an error in the html file: Exception Value: Related Field got invalid lookup: user_id The html files contains a simple {{ users }} tag. Comparing my code to the pizza example I don’t see what causes this error.

Kind regards,
Johanna

That’s because this clause:

Isn’t valid.

In a query, friends is the reference to the table related by the many-to-many relationship and not a reference to the join table.

(If you wanted to reference the join table in the query, you would use the query_name, user_friend.)

Django knows that when you’re referencing friends in a query that it needs to build the join through the join table.

This means that what you need is friends__id=request.user.id.

See the examples for Article and Publication at Many-to-many relationships | Django documentation | Django