Hossein Zare
Hossein Zare

Reputation: 580

Django inner join on

First of all, I want to say that i've checked all the similar questions and none of them was the answer to my question.

I have Friend and User models.

Friend Model

class Friend(models.Model):
    id = models.BigAutoField(primary_key=True)
    user = models.ForeignKey(User, on_delete=models.CASCADE, unique=False, related_name='%(class)s_requests_created')
    following = models.ForeignKey(User, on_delete=models.CASCADE, unique=False, null=True, blank=True)
    accepted = models.BooleanField(default=False)
    deleted_at = models.DateTimeField(null=True, blank=True)
    created_at = models.DateTimeField(auto_now_add=True)

User Model

class User(AbstractBaseUser):
    id = models.BigAutoField(primary_key=True)
    username = models.CharField(max_length=30, unique=True)
    email = models.EmailField(max_length=191, unique=True)
    first_name = models.CharField(max_length=30)
    last_name = models.CharField(max_length=30, blank=True)
    is_active = models.BooleanField(default=True)
    created_at = models.DateTimeField(auto_now_add=True)

Lets think of 3 users. User A, B, C. B is following A and C is following B. (I'm C) What i want to achieve is Get user A based on user B that is following user A.

Pretty simple query but a little bit complicated with Django QuerySet.

This is my query

SELECT U2.username FROM `accounts_friend`

INNER JOIN `accounts_user` U1 ON accounts_friend.following_id = U1.id AND U1.is_active = 1
INNER JOIN `accounts_friend` F1 ON accounts_friend.following_id = F1.user_id AND F1.accepted = 1 AND F1.deleted_at IS NULL AND F1.user_id != F1.following_id
INNER JOIN `accounts_user` U2 ON F1.following_id = U2.id AND U2.is_active = 1

WHERE accounts_friend.accepted = 1 AND accounts_friend.deleted_at IS NULL AND accounts_friend.user_id != accounts_friend.following_id

Here's what i've done but definitely not even close.

users = Friend.objects.filter(
    Q(user=request.user) & Q(accepted=True) & Q(deleted_at__isnull=True) & Q(following__is_active=True) & ~Q(following=request.user)
).annotate(
    username=F('following__friend__following__username')
).values('username')

If something is not clear, please let me know to add more details.

Upvotes: 1

Views: 84

Answers (2)

Iain Shelvington
Iain Shelvington

Reputation: 32294

The following query should give you all Users that are being followed by a user

User.objects.filter(
    is_active=True,
    friend__user=request.user,
    friend__accepted=True,
    friend__deleted_at__isnull=True
)

Upvotes: 1

willeM_ Van Onsem
willeM_ Van Onsem

Reputation: 477666

You can query for such User objects:

User.objects.filter(
    ~Q(friend__user_id=F('pk')),
    ~Q(friend__user__friend__user_id=F('friend__user_id')),
    is_active=True,
    friend__accepted=True,
    friend__deleted_at=None,
    friend__user__is_active=True,
    friend__user__friend__accepted=True,
    friend__user__friend__deleted_at=None,
    friend__user__friend__user=request.user
)

It is normally better to query for model objects, than for database values. Since model objects are "richer": they have defined behavior, and can easily be passed to perform processing.

Upvotes: 1

Related Questions