0

I have 3 models:

  • The django normal User model
  • Author model
  • UserAuthor models which is considered as a many-to-many relationship between the previous two models, but with additional fields (is_follow, review)

    class Author(models.Model):
        name = models.CharField(max_length=50)
    
    class UserAuthor(models.Model):
        user = models.ForeignKey(User, on_delete=models.CASCADE)
        author = models.ForeignKey(Author, on_delete=models.CASCADE, related_name='userauthors')
        is_follow = models.BooleanField(default=0)
        review = models.TextField(max_length=500, blank=True, null=True)
    

My question is about how to get all the authors with a field inside each author to show that the authenticated user is following him or not.

I know that I can use the related_name attribute, which returns the UserAuthor manager filtered by specific author, in some way to get it but how?!

My trial is:

class AuthorListView(ListView):
    model = Author

    def get_queryset(self):
        final_list = []
        authors_list = list(Author.objects.all())
        for author in authors_list:
            element = author.userauthors.filter(user=self.request.user)
            final_list.append(element)
        return final_list

I don't like this solution for sure, but how to do it in a better way?

Yasser Mohsen
  • 1,411
  • 1
  • 12
  • 29
  • Please take a look at https://stackoverflow.com/questions/44980322/getting-the-many-to-many-fields-of-a-many-to-many-object/44980459#44980459 – deathangel908 Sep 26 '17 at 15:14
  • I haven't tested myself, so I hesitate to upvote the linked answer, but the `annotate` answer to this question looks plausible to me: https://stackoverflow.com/questions/40599681/annotate-queryset-with-whether-matching-related-object-exists - annotating `when userauthors=self.request.user`. – Peter DeGlopper Sep 26 '17 at 15:24
  • @PeterDeGlopper The way from that answer to a solution to this question is not trivial. You'd want to annotate a boolean field if `userauthors__user=req.user` AND `userauthors__is_follow=True`, but those conditions would have to be met by the same `UserAuthor` instance... – user2390182 Sep 26 '17 at 15:32
  • I think you should be able to encapsulate that with a two-clause `When` - `Author.models.annotate(followed=Case(When(userauthors__user=self.request.user, userauthors__is_follow=True), ...))` - but again, untested – Peter DeGlopper Sep 26 '17 at 16:44

1 Answers1

1

As pointed out by PeterDeGlopper in the comments, you can use annotation:

from django.db.models import Case, When, Value, BooleanField
followed_ids = UserAuthor.objects.\
    filter(user=self.request.user, is_follow=True).\
    values_list('author_id', flat=True)
final_list = Author.objects.annotate(followed=Case(
    When(id__in=followed_ids, then=Value(True)),
    default=Value(False),
    output_field=BooleanField()
))

Now you can access the attribute followed for each instance of this queryset.

user2390182
  • 72,016
  • 6
  • 67
  • 89