Search code examples
djangodjango-rest-frameworkdjango-querysetdjango-3.2

Calling QuerySet.only() after union() is not supported


I'm a newbie to django here.

I'm getting the following error on my django backend:

Calling QuerySet.only() after union() is not supported

The error producing code is the following :

  post_notification_target_users = Post.get_post_comment_notification_target_users(post=post,
                                                                                     post_commenter=post_commenter).only(
        'id', 'username', 'notifications_settings__post_comment_notifications')

I searched where .union() is used with regards to the above and this is what I found :

def get_post_comment_notification_target_users(cls, post, post_commenter):
    """
    Returns the users that should be notified of a post comment.
    This includes the post creator and other post commenters
    :return:
    """

    # Add other post commenters, exclude replies to comments, the post commenter
    other_commenters = User.objects.filter(
        Q(posts_comments__post_id=post.pk, posts_comments__parent_comment_id=None, ) & ~Q(
            id=post_commenter.pk))

    post_creator = User.objects.filter(pk=post.creator_id)

    return other_commenters.union(post_creator)

I read other stack posts of what is allowed and what isn't after using union() but i'm unable to arrive at the right way to do it.


Solution

  • The simplest way to do this is to use your only() call before your call to union()

    other_commenters = User.objects.filter(
        Q(posts_comments__post_id=post.pk, 
          posts_comments__parent_comment_id=None, 
        ) & ~Q(id=post_commenter.pk)
        ).only(
        'id', 'username', 'notifications_settings__post_comment_notifications')
    
    post_creator = User.objects.filter(pk=post.creator_id).only(
        'id', 'username', 'notifications_settings__post_comment_notifications')
    
    return other_commenters.union(post_creator)
    

    Becuase you are filtering on the same object type (users) you could also extend your initial query, using parenthesis to indicate precedence, and avoid union() altogether.

    users_to_notify = User.objects.filter(
        (
            Q(posts_comments__post_id=post.pk, 
              posts_comments__parent_comment_id=None, 
             ) & ~Q(id=post_commenter.pk)
        ) | Q(pk=post.creator_id)
    )
    return users_to_notify
    

    NB: If you are sure that post_creator won't be in other_commenters, you can also avoid union() entirely with the OR operator.

    return other_commenters | post_Creator