JackJack
JackJack

Reputation: 193

Django - pass a list of results when querying using filter

In my FollowingPageView function, I'm trying to filter posts based on the logged in user's list of user's he/she is following.

You'll see the Profile model has a "following" field that captures the names of users the Profile owner is following. What I'm trying to do in my view is capture these names of the users in "following" then pass them to Post.objects.filter(created_by=user_list), but I will only get the last user in that list in this case. How can I iterate over the "user_list" Queryset and pass that to Post.objects.filter in order to return the posts from each user in that list? In this case, I should have two users in the Queryset [<User: winter>, <User: daisy>].

models.py

class Profile(models.Model):
    user = models.OneToOneField(User, null=True, on_delete=models.CASCADE)
    bio = models.TextField(null=True, blank=True)
    website = models.CharField(max_length=225, null=True, blank=True)
    follower = models.ManyToManyField(User, blank=True, related_name="followed_user") # user following this profile
    following = models.ManyToManyField(User, blank=True, related_name="following_user") # profile user that follows this profile
    
    def __str__(self):
        return f"{self.user}'s' profile id is {self.id}"
        
    def following_users(self):
        for username in self.following:
            return username
        
    def get_absolute_url(self):
        return reverse("network:profile-detail", args=[str(self.id)])

class Post(models.Model):
    created_by = models.ForeignKey(User, on_delete=models.CASCADE)
    subject = models.CharField(max_length=50)
    body = models.TextField(max_length=1000)
    timestamp = models.DateTimeField(auto_now_add=True)
    likes = models.ManyToManyField(User, blank=True, related_name="posts")

    def __str__(self):
        return f"{self.created_by} posted {self.body}"

views.py

# Following Users
def FollowingPageView(request, pk):
    profile = get_object_or_404(Profile, id=pk)
    user_list = []
    
    for user in profile.following.all():
        user_list.append(user)
    posts = Post.objects.filter(created_by=user_list[0])
    print(user_list)
        
    paginator = Paginator(posts, 10)
    
    page_number = request.GET.get("page")
    page_obj = paginator.get_page(page_number)
    
    try:
        if request.method == "GET":
            return render(request, "network/follow-posts.html", { "profile": profile, "page_obj": page_obj })
    except ValueError:
        return render(request, "network:index.html", {"error": ValueError})

Upvotes: 0

Views: 651

Answers (1)

Peter DeGlopper
Peter DeGlopper

Reputation: 37319

One approach is to use an __in query. Here, because you're not using user_list for anything else, you'll probably get the best results from using an inner query:

posts = Post.objects.filter(created_by__in=profile.following.all())

But note the performance advice in the linked docs - test it on your actual setup and see.

Possibly with a distinct() call required, I can't remember exactly what triggers the possibility of duplicate records with many-to-many fields.

There are other ways to express it using field references, something like:

posts = Post.objects.filter(created_by__profile__followed_user=profile.user).distinct() 

Backing databases tend to do that with a join rather than a subquery, so it can have different performance characteristics.

Upvotes: 1

Related Questions