Jason B
Jason B

Reputation: 365

Django if statement to check value in manytomanyfield in for loop in template

In my project, users have a manytomanyfield of users they are following. When they look at the list of users who are following them, I want to show a Follow/Unfollow link depending on if they have that person in their following list. For example, if B is following A, and A is following B, then there should be an Unfollow link next to B's name when A views their following list.

unfortunately, it always says "(Follow)" and never gives me the "(Remove)" link even if I'm logged in as a user that's already following that user.

My userprofile model:

class UserProfile(models.Model):
    user = models.OneToOneField(User, related_name='user_object')
    visible = models.BooleanField(default=True)
    bio = models.TextField(null=True, blank=True)
    avatar = models.FileField(upload_to=get_upload_file_name, null=True, blank=True)
    following = models.ManyToManyField(User, related_name='following_list')

and the code I am trying to use in my template:

{% for f in followers %}
    <a href="/accounts/profile/{{f.user.id}}/">{{f.user.username}}</a> 
    {% if thisuser.user_object.following.all == f.user %}
        <a href="/accounts/profile/{{f.user.id}}/unfollow/">(Remove)</a>
    {% else %}
        <a href="/accounts/profile/{{f.user.id}}/follow/">(Follow)</a>
    {% endif %}
{% endfor %}

In my views, I am sending:

def followers(request, user_id=1):
    thisuser = request.user
    userlookup = User.objects.get(id=user_id)
    following = thisuser
    followers = UserProfile.objects.filter(following=thisuser)

    args = {'following': following, 'thisuser': thisuser, 'userlookup': userlookup, 'followers': followers}
    args.update(csrf(request))

    return render_to_response('followers.html', args)

Upvotes: 1

Views: 1019

Answers (1)

Burhan Khalid
Burhan Khalid

Reputation: 174622

Decorate your objects in your view to make life in your template easier:

from django.shortcuts import render

def followers(request, user_id=1):

    user = User.objects.get(pk=user_id)
    followers = UserProfile.objects.filter(following=request.user)

    args = {}
    args['following'] = request.user
    args['userlookup'] = User.objects.get(pk=user_id)
    args['followers'] = []

    for f in followers:
        user_status = (f, request.user.user_object.following_list.filter(pk=f.pk).exists())
        args['followers'].append(user_status)

    return render(request, 'followers.html', args)

The key part is this:

(f, request.user.user_object.following_list.filter(pk=f.pk).exists())

This is a 2-tuple, the first item is the user profile object, and the second item is either True or False if this user is being followed. The exists() queryset method returns True if the query would have returned results. I use this to flag each user object.

I then collect this "decorated" lists of user profile objects in a list, which is sent as the context variable followers to the template.

You should avoid doing complex logic in the templates, and whenever possible use the views to send extra information about your objects. This not only enhances the performance of your application (templates are not optimal for heavy processing and the most difficult part to debug); but also keeps your templates free from any logic - beyond what is required to display things.

Now, in your template:

{% for f,following in followers %}
    <a href="/accounts/profile/{{f.user.id}}/">{{f.user.username}}</a> 
    {% if following %}
        <a href="/accounts/profile/{{f.user.id}}/unfollow/">(Remove)</a>
    {% else %}
        <a href="/accounts/profile/{{f.user.id}}/follow/">(Follow)</a>
    {% endif %}
{% endfor %}

You don't need to pass request.user to the template, instead make sure the request context processor is enabled in your settings:

import django.conf.global_settings as DEFAULT_SETTINGS

TEMPLATE_CONTEXT_PROCESSORS += DEFAULT_SETTINGS.TEMPLATE_CONTEXT_PROCESSORS+(
    'django.core.context_processors.request',
)

Upvotes: 1

Related Questions