68

How to check in template whether user belongs to some group?

It is possible in a view which is generating the template but what if I want to check this in base.html which is an extending template (it does not have it's own view function)?

All of my templates extends base.html so it is not good to check it in each view.

The base.html contains upper bar, which should contain buttons depending on in which group logged user is (Customers, Sellers).

In my base.html is:

{% if user.is_authenticated %}

which is not enough because I have to act differently to users from Customers and users from Sellers.

So the thing I want is:

{% if user.in_group('Customers') %}
 <p>Customer</p>
{% endif %}
{% if user.in_group('Sellers') %}
<p>Seller</p>
{% endif %}
Milano
  • 18,048
  • 37
  • 153
  • 353

11 Answers11

112

You need custom template tag:

from django import template

register = template.Library() 

@register.filter(name='has_group') 
def has_group(user, group_name):
    return user.groups.filter(name=group_name).exists() 

In your template:

{% if request.user|has_group:"mygroup" %} 
    <p>User belongs to my group 
{% else %}
    <p>User doesn't belong to mygroup</p>
{% endif %}

Source: http://www.abidibo.net/blog/2014/05/22/check-if-user-belongs-group-django-templates/

Docs: https://docs.djangoproject.com/en/dev/howto/custom-template-tags/

abumalick
  • 2,136
  • 23
  • 27
mishbah
  • 5,487
  • 5
  • 25
  • 35
  • 4
    Mishmash, thank you. I've read that web page but I don't know where this code should be. – Milano Jan 03 '16 at 09:42
  • 1
    How would you handle the case where `Group.objects.get(name=group_name)` doesn't return an object? – danielmaxx Nov 22 '16 at 23:00
  • 3
    @danielmaxx You could get a bool by doing `Group.objects.filter(name=group_name).exists()` – Max Lap Mar 03 '17 at 21:30
  • 4
    To avoid multiple queries I'd suggest using `return user.groups.filter(name=group_name).exists()` – antonagestam Sep 17 '17 at 19:24
  • Keep in mind the performance impact: For 5 records: 2.33 ms (5 queries ) before.....6.71 ms (15 queries including 10 duplicates ) after – tread Oct 04 '17 at 13:01
  • If the concern is avoiding extra queries, that can be wrapped in `lru_cache` to make repetitive calls more efficient while still avoiding the extra queries for each group check. – Chris Adams Sep 18 '18 at 19:09
  • 1
    Is it correct that the `from django.contrib.auth.models import Group` is not necessary here? – Clement H. Jan 20 '19 at 22:19
  • 8
    You forgot about {% load has_group %} in html template – dzierzak Mar 14 '20 at 08:36
  • 1
    I would suggest wrapping it with a `try-catch` to avoid errors when the group is not defined in the DB. The catch will always return `False` – Joel Carneiro Apr 14 '21 at 15:04
35

In your app create a folder 'templatetags'. In this folder create two files:

__init__.py

auth_extras.py

from django import template
from django.contrib.auth.models import Group 

register = template.Library()

@register.filter(name='has_group')
def has_group(user, group_name): 
    group = Group.objects.get(name=group_name) 
    return True if group in user.groups.all() else False

It should look like this now:

app/
    __init__.py
    models.py
    templatetags/
        __init__.py
        auth_extras.py
    views.py

After adding the templatetags module, you will need to restart your server before you can use the tags or filters in templates.

In your base.html (template) use the following:

{% load auth_extras %}

and to check if the user is in group "moderator":

{% if request.user|has_group:"moderator" %} 
    <p>moderator</p> 
{% endif %}

Documentation: https://docs.djangoproject.com/en/1.11/howto/custom-template-tags/

mohammedgqudah
  • 568
  • 4
  • 15
fuser60596
  • 1,087
  • 1
  • 12
  • 26
17

I'd say that the best way is:

yourapp/templatetags/templatetagname.py

from django import template

register = template.Library()

@register.filter(name='has_group')
def has_group(user, group_name):
    return user.groups.filter(name=group_name).exists()

yourapp/templates/yourapp/yourtemplate.html:

{% load has_group %}

{% if request.user|has_group:"mygroup" %} 
    <p>User belongs to my group</p>
{% else %}
    <p>User does not belong to my group</p>
{% endif %}

EDIT: added line with template tag loading as was advised in comments.

EDIT2: fixed minor typo.

Alex K.
  • 835
  • 6
  • 15
  • 3
    It's worth mentioning you will need to include {% load templatetagname %} at the top of each HTML template where you will want to use it. – pawisoon Feb 14 '19 at 09:34
  • **@pawisoon** That's the issue, and why I will not use it. Since I got 10 templates, and I might make more later. – AnonymousUser Sep 29 '21 at 06:40
13

Watch out that you'll get an exception if the group does not exist in the DB.

The custom template tag should be:

from django import template
from django.contrib.auth.models import Group

register = template.Library()

@register.filter(name='has_group')
def has_group(user, group_name):
    try:
        group =  Group.objects.get(name=group_name)
    except Group.DoesNotExist:
        return False

    return group in user.groups.all()

Your template:

{% if request.user|has_group:"mygroup" %} 
    <p>User belongs to my group 
{% else %}
    <p>User doesn't belong to mygroup</p>
{% endif %}
muccix
  • 321
  • 1
  • 3
  • 10
  • This is a better answer because it's using a try/catch block and won't error in your template if the user isn't part of the group. – Kalob Taulien Jan 21 '18 at 19:23
10

In your template

{% ifequal user.groups.all.0.name "user" %}
  This is User
{% endifequal %}
  
Jaroslav Hájek
  • 109
  • 1
  • 2
  • 3
    This works beautifully if the user belongs to a single group. If a user belongs to multiple groups, is there an easy way to modify this so we are not checking against just all.0.name? – LNI Oct 16 '17 at 17:51
  • @LNI I am looking for the same answer. I don't want to hard code any group name . If someone decides to change the group, name, think about the rework. – Arindam Roychowdhury Dec 20 '18 at 07:30
6

You can use this:

{% for group_for in request.user.groups.all %}
    {% if group_for.name == 'Customers' %}
        Text showed to users in group 'Customers'
    {% elif group_for.name == 'Sellers' %}
        Text showed to users in group 'Sellers'
    {% endif %}
{% endfor %}

This is iterating through groups related to the user who makes the request and printing the text if the name of the iterated group equals 'Customers', 'Sellers', etc

Tadeo
  • 431
  • 7
  • 11
4

The easiest way that I found is by adding all groups name to the context by using a context_preprocessor

In your app create a file context_processors.py and add the following content:

def user_groups_processor(request):
    groups = []
    user = request.user
    if user.is_authenticated:
        groups = list(user.groups.values_list('name',flat = True))
    return {'groups': groups}

in your settings, add the new context processor

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [],
        'APP_DIRS': True,
        'OPTIONS': {
            # ... some options here ...
             "context_processors": [
                "my_app.context_processors.user_groups_processor"
            ],
        },
    },
]

Or if you prefer in settings.py

TEMPLATES[0]['OPTIONS']['context_processors'].append("my_app.context_processors.user_groups_processor")

After that in your templates you can use:

{% if 'vip' in groups %}
  <p>Paragraph only visible to VIPs</p>
{% endif %}
abumalick
  • 2,136
  • 23
  • 27
  • It is for me the best answer. I did custom filter to get the current user groups before, but it makes a DB request every time the tag is used in a template. For some templates, I need to check the role in many places of the template, resulting in many DB request, for the same information. Context processor makes more sense for my needs. – Quentin Feb 12 '21 at 10:10
2
{% if target_group in user.groups.all.0.name %}
    # do your stuff
{% endif %}
scorpionipx
  • 107
  • 5
  • Can you provide an explanation for your answer? – rassar Oct 22 '19 at 18:49
  • 2
    This approach works even if the user belongs to multiple groups. So it just checks if a particular group (target_group) is among user's groups. Notice: target_group is the name of the group, not a Group object! – scorpionipx Oct 25 '19 at 10:59
  • Your answer only checks the first item because you are using index `0`. If you want to do it in the template, you might be better off looping through the related group objects and then matching the name property. `{% for group in user.groups.all %} {%if group.name == target_group %} Do smthg {%endif%} {%endfor%}` – Codebender Mar 09 '20 at 06:26
  • @Codebender But if you have 2 groups, you can't make `if group.name == target_group1 and group.name == target_group2` It will always return False, even if a user has both those groups, because the for loop checks one at a time. – AnonymousUser Sep 29 '21 at 06:54
1

Although the answer given by mishbah is right but it didn't work for me.

I am using Django 2.2.7 and i figured out that register = template.Library() should be replaced with from django.template.defaultfilters import register.

i hope someone will find it useful.

iqbal
  • 29
  • 4
1

In my case the problem was, I was using {% load filter_method_name %}

I had to change to {% load filename %}

For example,

app/
    __init__.py
    models.py
    templatetags/
        __init__.py
        auth_extras.py
    views.py

Here, template taq will be {% load auth_extras %}

I then had to restart the server.

0

First You need to define a custom filter function inside has_group.py

from django import template
from xx.models import Xuser


register = template.Library()


@register.filter(name='has_group')
def has_group(user, group_name):
    try:
        group = Xuser.objects.get(email=user.email)
        if group.role == group_name:
            return True
        else:
            return False
    except Xuser.DoesNotExist:
        return False

    return group

 

in django settings.py file you need to add

 'libraries': {
                'my_templatetag': 'xx.templates.has_group',

            },

inside TEMPLATES = []

and then add

{% load my_templatetag %}

in your example.html

in last

{% if user|has_group:"admin" %} 
 {% endif %}