2

I have a model for followers. Here is the model:

class Follow(models.Model):
    followee = models.ForeignKey(User, on_delete=models.CASCADE, related_name="followee")

    follower = models.ForeignKey(User, on_delete=models.CASCADE, related_name="follower")

    created_at = models.DateTimeField(auto_now_add=True, verbose_name="created at")
    updated_at = models.DateTimeField(auto_now=True, verbose_name="updated at")

    class Meta:
        unique_together = ["followee", "follower"]

    def __str__(self):
        return "{} is following {}".format(self.follower, self.followee)

    def save(self, *args, **kwargs):
        if self.followee == self.follower:
            return "You cannot follow yourself"
        else:
            super().save(*args, **kwargs)

Users create multiple types of objects like posts and questions. I would like to show all the posts and questions of all the users that follow a specific user. Simplified show me all the posts and questions of users that follow me.

I am using a module called drf_multiple_model and here is my view, which I cannot get to work. It gives me the following error that I don't understand:

Cannot use QuerySet for "Follow": Use a QuerySet for "User".

Here is the view I am using:

def get_followers(queryset, request, *args, **kwargs):
    id = kwargs['user']
    user = User.objects.get(id=id)
    followers = Follow.objects.all().filter(followee=user)
    return queryset.filter(user__in=followers)

class HomeView(FlatMultipleModelAPIView):
    permission_classes = [IsAuthenticated]

    def get_querylist(self):

        querylist = [ 
            {'queryset':Post.objects.all(), 'serializer_class': UserPostSerializer, 'filter_fn': get_followers, 'label':'post'},
            {'queryset':Question.objects.all(), 'serializer_class': QuestionSerializer, 'filter_fn': get_followers, 'label':'question'},
        ]
        return querylist

What am I doing wrong please?

Selcuk
  • 57,004
  • 12
  • 102
  • 110
crawlingdev
  • 212
  • 9
  • Looks interesting. Have you tried evaluating the queryset using `followers = list(Follow.objects.all().filter(followee=user))` to see if it works? – Selcuk Jan 08 '23 at 08:59
  • Oh, of course. It's hard to explain it in a comment, see my answer. – Selcuk Jan 08 '23 at 09:17

2 Answers2

1

In order to be able to use the __in filter the followers should be an iterable of Users. Try this:

followers = [f.follower for f in Follow.objects.filter(followee=user)]

or

followers = [f.follower for f in user.follower.all()]
Selcuk
  • 57,004
  • 12
  • 102
  • 110
  • How do I do it for friends because friends are both ways but the model only shows one way. So for example if user1 is friends with user2 then obviously user2 is also friends with user1 but the model only shows it one way so I need to swap them around if you get what I'm saying. I tried this but it's not working: friends = [f.friend for f in Friendship.objects.all().filter(Q(person=user) | Q(friend=user))] for friendship in friends: if friendship.friend == user: friendship.person, friendship.friend = friendship.friend, friendship.person – crawlingdev Jan 09 '23 at 16:17
  • You should use a self referencing many-to-many relation for that. See [this](https://stackoverflow.com/questions/11721157/django-many-to-many-m2m-relation-to-same-model) for a similar question. – Selcuk Jan 09 '23 at 23:21
1

You can filter with a JOIN, like:

def get_followers(queryset, request, *args, **kwargs):
    return queryset.filter(user__follower__followee_id=kwargs['user'])

This will fetch the items in a single query, and thus avoid first querying the followers and then obtain the items.

Willem Van Onsem
  • 443,496
  • 30
  • 428
  • 555
  • Thanks Willem I will try it and I really appreciate you showing me another way of doing things – crawlingdev Jan 08 '23 at 11:00
  • Hi Willem if you don't mind how do I do it for friends because friends are both ways but the model only shows one way. So for example if user1 is friends with user2 then obviously user2 is also friends with user1 but the model only shows it one way so I need to swap them around if you get what I'm saying. I tried this but it's not working: friends = [f.friend for f in Friendship.objects.all().filter(Q(person=user) | Q(friend=user))] for friendship in friends: if friendship.friend == user: friendship.person, friendship.friend = friendship.friend, friendship.person – crawlingdev Jan 09 '23 at 16:52
  • @crawlingdev: you "walk" a relation in the opposite way using the `related_query_name` of the `ForeignKey`/`OneToOneField`/`ManyToManyField`, if that is not speicified, it uses the `related_name`, and if that is not specified, it uses the name of the model in lowercase. – Willem Van Onsem Jan 09 '23 at 16:55
  • Thanks for the comment. Now I'm even more confused ha ha. I have to read it like ten times and try and figure out what you mean. Nobody can say coding is easy ha ha – crawlingdev Jan 09 '23 at 17:01
  • Cannot get my head around this would you mind maybe simplifying it? – crawlingdev Jan 10 '23 at 02:27