39

I would like to display the list of the authenticated users.

On the documentation: http://docs.djangoproject.com/en/dev/topics/auth/

class models.User
is_authenticated()¶
Always returns True. This is a way to tell if the user has been authenticated. ...

You can know on the template side is the current User is authenticated or not:

{% if user.is_authenticated %} {% endif %}

But I didn't found the way the get the list of the authenticated users.

Any idea?

Bill the Lizard
  • 398,270
  • 210
  • 566
  • 880
GeReinhart
  • 423
  • 1
  • 5
  • 8

4 Answers4

79

Going along with rz's answer, you could query the Session model for non-expired sessions, then turn the session data into users. Once you've got that you could turn it into a template tag which could render the list on any given page.

(This is all untested, but hopefully will be close to working).

Fetch all the logged in users...

from django.contrib.auth.models import User
from django.contrib.sessions.models import Session
from django.utils import timezone

def get_all_logged_in_users():
    # Query all non-expired sessions
    # use timezone.now() instead of datetime.now() in latest versions of Django
    sessions = Session.objects.filter(expire_date__gte=timezone.now())
    uid_list = []

    # Build a list of user ids from that query
    for session in sessions:
        data = session.get_decoded()
        uid_list.append(data.get('_auth_user_id', None))

    # Query all logged in users based on id list
    return User.objects.filter(id__in=uid_list)

Using this, you can make a simple inclusion template tag...

from django import template
from wherever import get_all_logged_in_users
register = template.Library()

@register.inclusion_tag('templatetags/logged_in_user_list.html')
def render_logged_in_user_list():
    return { 'users': get_all_logged_in_users() }

logged_in_user_list.html

{% if users %}
<ul class="user-list">
    {% for user in users %}
    <li>{{ user }}</li>
    {% endfor %}
</ul>
{% endif %}

Then in your main page you can simply use it where you like...

{% load your_library_name %}
{% render_logged_in_user_list %}

EDIT

For those talking about the 2-week persistent issue, I'm assuming that anyone wanting to have an "active users" type of listing will be making use of the SESSION_EXPIRE_AT_BROWSER_CLOSE setting, though I recognize this isn't always the case.

Thomas Gak-Deluen
  • 2,759
  • 2
  • 28
  • 38
T. Stone
  • 19,209
  • 15
  • 69
  • 97
  • Thanks a lot, it was exactly what i was looking for. Gerald – GeReinhart Apr 27 '10 at 18:52
  • Um, just so you know "non-expired sessions" will give you everyone who has been logged in for *two weeks*… Look at django-tracking instead if you would like currently active users. –  Apr 27 '10 at 19:34
  • same comment as @jonwd7. It is probably better to check for last_login or some more sophisticated filtering. 2 weeks hardly counts as "currently" – rz. Apr 27 '10 at 20:13
  • 1
    You could probably throw together some middleware that sets a session variable to timestamp the most recent request for all users and then filter on those less than 5 minutes old. Then the above solution would work pretty well. – DrBloodmoney Apr 28 '10 at 12:04
  • I was using the `get_all_logged_in_users` to get active users but it stops working a few days ago? using `Python(2.7)` and `Django(1.11)`. – Abdul Rehman Jul 14 '19 at 06:03
8

Most reliable solution would only be the something you store when user logs in or logs out. I saw this solution and i think its worth sharing.

models.py

from django.contrib.auth.signals import user_logged_in, user_logged_out

class LoggedUser(models.Model):
    user = models.ForeignKey(User, primary_key=True)

    def __unicode__(self):
        return self.user.username

    def login_user(sender, request, user, **kwargs):
        LoggedUser(user=user).save()

    def logout_user(sender, request, user, **kwargs):
        try:
            u = LoggedUser.objects.get(user=user)
            u.delete()
        except LoggedUser.DoesNotExist:
            pass

    user_logged_in.connect(login_user)
    user_logged_out.connect(logout_user)

views.py

logged_users = LoggedUser.objects.all().order_by('username')
A.J.
  • 8,557
  • 11
  • 61
  • 89
  • 2
    I copy-pasted your code and it works pretty good, even better than the accepted answer because in some cases it didn't detected when users log-out (but I have modified the User class). About your solution, to get the real logged_users I had to do: `logged_users=[user.user for user in LoggedUser.objects.all()]` –  Aug 15 '15 at 16:24
  • NameError: name 'user_logged_in' is not defined – fakeMake Nov 19 '21 at 17:29
  • 1
    Its a django signal, i have updated the answer to include the import. thanks. – A.J. Nov 23 '21 at 10:46
3

Sounds similiar with this solution, you can create a custom middleware to do it. I found awesome OnlineNowMiddleware here.

Where you will get these;

  1. {{ request.online_now }} => display all list of online users.
  2. {{ request.online_now_ids }} => display all online user ids.
  3. {{ request.online_now.count }} => display total online users.

How to set up?

Create file middleware.py where location of settings.py has been saved, eg:

projectname/projectname/__init__.py
projectname/projectname/middleware.py
projectname/projectname/settings.py

Then following this lines;

from django.core.cache import cache
from django.conf import settings
from django.contrib.auth.models import User
from django.utils.deprecation import MiddlewareMixin

ONLINE_THRESHOLD = getattr(settings, 'ONLINE_THRESHOLD', 60 * 15)
ONLINE_MAX = getattr(settings, 'ONLINE_MAX', 50)


def get_online_now(self):
    return User.objects.filter(id__in=self.online_now_ids or [])


class OnlineNowMiddleware(MiddlewareMixin):
    """
    Maintains a list of users who have interacted with the website recently.
    Their user IDs are available as ``online_now_ids`` on the request object,
    and their corresponding users are available (lazily) as the
    ``online_now`` property on the request object.
    """

    def process_request(self, request):
        # First get the index
        uids = cache.get('online-now', [])

        # Perform the multiget on the individual online uid keys
        online_keys = ['online-%s' % (u,) for u in uids]
        fresh = cache.get_many(online_keys).keys()
        online_now_ids = [int(k.replace('online-', '')) for k in fresh]

        # If the user is authenticated, add their id to the list
        if request.user.is_authenticated:
            uid = request.user.id
            # If their uid is already in the list, we want to bump it
            # to the top, so we remove the earlier entry.
            if uid in online_now_ids:
                online_now_ids.remove(uid)
            online_now_ids.append(uid)
            if len(online_now_ids) > ONLINE_MAX:
                del online_now_ids[0]

        # Attach our modifications to the request object
        request.__class__.online_now_ids = online_now_ids
        request.__class__.online_now = property(get_online_now)

        # Set the new cache
        cache.set('online-%s' % (request.user.pk,), True, ONLINE_THRESHOLD)
        cache.set('online-now', online_now_ids, ONLINE_THRESHOLD)

Finally update your MIDDLEWARE inside file of projectname/projectname/settings.py:

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.cache.UpdateCacheMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.cache.FetchFromCacheMiddleware',
    ....
    # Custom middleware
    'projectname.middleware.OnlineNowMiddleware',
]

For other condition, you can also check the current user is online or not with:

{% if request.user in request.online_now %}
    {# do stuff #}
{% endif %}
binpy
  • 3,994
  • 3
  • 17
  • 54
  • Any idea how to modify this so that is removes users from online_now when they log out ? – Paddy Popeye May 21 '18 at 13:48
  • @PaddyPopeye by default, it handled with `ONLINE_THRESHOLD`. So, after users sign out for `60 * 15` => 15 minutes automaticaly deleted from `online_now`. – binpy May 22 '18 at 03:45
  • thks for the answer. but the problem with the code is that even though ONLINE_THRESHOLD handles this, it is not reflected on the page. Only after the page is refreshed manually do you see that a user has logged out.. any suggestions – Paddy Popeye May 29 '18 at 13:50
  • @PaddyPopeye If what you need is realtime method, perhaps you can handle it with ajax post inside `setInterval`. eg: https://pastebin.com/3xVFpJiB – binpy May 30 '18 at 02:03
  • this solution looks good, I am thinking about what could be the drawbacks of this solution? Also, the accepted answer involves querying the session model, which is a db hit. Any thoughts on this? – Shahrukh Mohammad Jul 08 '18 at 13:52
  • I have a problem with that solution. I opened 2 sessions: one in the normal tab and another one in incognito mode. I wait over 15 mins and the user still remains online. Any fix for that? – pawisoon Oct 28 '19 at 09:41
  • What is the purpose of ```ONLINE_MAX = getattr(settings, 'ONLINE_MAX', 50)```? It only counts 50 users? – There Jul 10 '21 at 00:28
  • 1
    @There yes, meanwhile you can remove it methods if needed. – binpy Jul 10 '21 at 23:55
2

There is no easy, built-in way to do what you want that I know of. I'd try a combination of expiring sessions and filtering on last_login. Maybe even write a custom manager for that.

rz.
  • 19,861
  • 10
  • 54
  • 47