Frederik Vanclooster
Frederik Vanclooster

Reputation: 513

Django Query: How to find all posts from people you follow

I'm currently building a website with the Django Framework. I want on the homepage of my website to display all posts made by people the user is following. Here are the classes for Profile, Story and Follow:

class Profile(AbstractBaseUser, PermissionsMixin):
   email = models.EmailField(unique=True)
   first_name = models.CharField(max_length=30, null=True)
   last_name = models.CharField(max_length=30, null=True)

class Follow(models.Model):
   following = models.ForeignKey('Profile', on_delete=models.CASCADE, related_name="following")
   follower = models.ForeignKey('Profile', on_delete=models.CASCADE, related_name="follower")
   follow_time = models.DateTimeField(auto_now=True)

class Story(models.Model):
   author = models.ForeignKey('accounts.Profile', on_delete=models.CASCADE, related_name="author")
   title = models.CharField(max_length=50)
   content = models.TextField(max_length=10000)

As you can see Follow uses two Foreign Keys to represent the following and the follower. Is there a way to query all stories from people the user is following?

I really don't know what to filter. Or is this maybe a job for aggregation? If someone could help me, that would be awesome!

following_feed = Story.object.filter(???).order_by('-creation_date')

Upvotes: 4

Views: 2241

Answers (2)

willeM_ Van Onsem
willeM_ Van Onsem

Reputation: 476709

One can use double underscores (__) to look "through" relations (like ForeignKeys, etc.).

So here we can filter like:

Story.objects.filter(
    author__following__follower=my_profile
)

So by using author we obtain a reference to the Profile of the author, then with following we look at the Follow model and then finally with follower we again obtain a reference to Profile(s): the profile(s) of the follower(s).

my_profile of course need to be substituted with a Profile object (the profile of the person that is a follower of the authors of the Storys you wish to obtain).

This will generate a query like:

SELECT s.*
FROM story AS s
JOIN follow AS f ON f.following_id = s.author_id
WHERE f.follower_id = 123

where 123 is the id of the my_profile.

If a person is following another person multiple times (here this can happen since you do not enforce that the follower, following tuples are unique in the Follow model), then the corresponding Storys will be yielded multiple times.

It is therefore probably better to add a unique_together constraint in the Follow model:

class Follow(models.Model):
    following = models.ForeignKey(
        'Profile',
        on_delete=models.CASCADE,
        related_name="following"
    )
    follower = models.ForeignKey(
        'Profile',
        on_delete=models.CASCADE,
        related_name="follower"
    )
    follow_time = models.DateTimeField(auto_now=True)

    class Meta:
        unique_together = (('following', 'follower'), )

It might also be worth to see the Follow model as the through model of a ManyToManyField [Django-doc].

Upvotes: 7

Ramtin
Ramtin

Reputation: 3205

Note that I haven't tested the code I am posting so tell me if something is missing.
First, you need to get all the Profiles that your users follow. Then you have to get the Stories that they have.

followed_people = Follow.objects.filter(follower=current_user).values('following')
stories = Story.objects.filter(author__in=followed_people) 

Upvotes: 2

Related Questions