How to filter this ManyToMany relation?

I’ve been trying to work this out for a couple days, but no success. I have a simple social network project for learning purposes and these are the models:

class User(AbstractUser):
    profile_picture = models.CharField(max_length=100, default="https://cdn.onlinewebfonts.com/svg/img_568656.png")
    followers = models.ManyToManyField('self', blank=True)
    following = models.ManyToManyField('self', blank=True)
    join_date = models.DateTimeField(auto_now_add=True)
    def serialize(self):
        return {
            "id": self.id,
            "username": self.username,
            "email": self.email,
            "followers": [user.username for user in self.followers.all()],
            "following": [user.username for user in self.following.all()],
            "join_date": self.join_date.strftime("%b. %d, %Y, %H:%M %p"),
            "profile_picture": self.profile_picture
        }
    
class Post(models.Model):
    autor = models.ForeignKey(User, on_delete=models.CASCADE, null=True, blank=True, default="", related_name="user_post")
    content = models.TextField(max_length=240)
    likers = models.ManyToManyField(User, related_name="posts_liked")
    timestamp = models.DateTimeField(auto_now_add=True)
    def serialize(self):
        return {
            "id": self.id,
            "autor": self.autor.username,
            "content": self.content,
            "likers": [user.username for user in self.likers.all()],
            "timestamp": self.timestamp.strftime("%b. %d, %Y, %H:%M %p"),
            "autor_profile_pic": self.autor.profile_picture
        }

As you can see, every user can follow and be followed in the social media. What I’m stuck on is getting all the posts made by all the users that the user follows. In other words, I want to make a page that behaves just like the Twitter feed page, where you can see the posts made by people you follow. What I tried so far was this query:

def following(request, user_username):
     user = User.objects.get(username=user_username)
     try:
         following_users = user.following.all()
         following_posts = []
         for following_user in following_users:
              following_posts.append(Post.objects.filter(username=following_user.username))
     except:
         return JsonResponse({"error": "There was an error loading the following posts"}, status=400)
     following_posts = following_posts.order_by("-timestamp").all()
     return JsonResponse([post.serialize() for post in following_posts], safe=False)

I tried first getting all the users that the current user follows (user.following.all()) and then getting all the posts they have (Post.objects.filter(username=following_user.username) in the for loop) and appending them to a list called following_posts. But this doesn’t work because the order_by function only works with QuerySets and not lists. The error I got when running this code made me think that this was not the best way of making this query, but I can’t think of another way. Can anyone give me an idea? Thanks in advance

It’s better to do the relationship traversing in the database rather than iterating over the collections in python (checkout these docs for more information on traversing relationships). This will reduce the number of queries and generally increase the overall performance. Plus it’s a little easier to reason when using it.

followed_posts = Post.objects.filter(autor__followers__username=user_username)

Depending on your requirements. You may not need two ManyToMany fields to identify a user’s followers and those the user follows.

The adjusted user model would look like so:

class User(AbstractUser):
    profile_picture = models.CharField(max_length=100, default="https://cdn.onlinewebfonts.com/svg/img_568656.png")
    followers = models.ManyToManyField('self', blank=True, related_name="following")
    join_date = models.DateTimeField(auto_now_add=True)

Now when you make user_a follow user_b, user_b's followers will include user_a.

user_a.following.add(user_b)
assert user_b.followers.filter(id=user_a.id).exists()
2 Likes