Evgenii Legotckoi
02 вересня 2019 р. 17:31

Django - Урок 048. Як додати статус онлайн на сайті

Довгий час не додавав подібний функціонал на сайті з тієї простої причини, що не знав, з якого б боку до цього підійти, оскільки хотілося б обробляти цю інформацію в одному єдиному місці, а не в кожному View.

Згодом я почав модифікувати Backend класи для сайту і рішення прийшло саме собою. Потрібно було всього лише модифікувати кожен бекенд аутентифікації, який використовується на сайті. І переписати метод get_user, в якому буде зберігатися інформація про останньому запиті користувача до сайту. А статус онлайн можна зберігати останні 15 хвилин, потім статус буде оффлайн.

А дату останнього запиту можна зберігати або в профілі користувача, який буде OneToOne моделлю до користувача, або перевизначити модель користувача. Я вибрав варіант з перевизначенням моделі користувача.


my_auth

Створимо наш власний модуль аутентифікації my_auth, якщо ви його ще не створили

  1. python manage.py startapp my_auth

models.py

Після чого перевизначити модель користувача

  1. # -*- coding: utf-8 -*-
  2.  
  3. from django.contrib.auth.models import AbstractUser
  4. from django.contrib.humanize.templatetags.humanize import naturaltime
  5. from django.db import models
  6. from django.utils import timezone
  7. from django.utils.translation import ugettext_lazy as _
  8.  
  9.  
  10. class User(AbstractUser):
  11.  
  12. last_online = models.DateTimeField(blank=True, null=True)
  13.  
  14. # В даному методі перевірка, що дата останнього відвідування не старше 15 хвилин
  15. def is_online(self):
  16. if self.last_online:
  17. return (timezone.now() - self.last_online) < timezone.timedelta(minutes=15)
  18. return False
  19.  
  20. # Якщо користувач відвідував сайт не більше 15 хвилин назад,
  21. def get_online_info(self):
  22. if self.is_online():
  23. # то повертаємо інформацію, що він онлайн
  24. return _('Online')
  25. if self.last_online:
  26. # інакше пишемо повідомлення про останньому відвідуванні
  27. return _('Last visit {}').format(naturaltime(self.last_online))
  28. # Якщо ви тільки недавно додали інформацію про відвідування користувачем сайту
  29.             # То для деяких користувачів інфомація про відвідування може і не бути, повернемо інформацію, що останній візит невідомо
  30. return _('Unknown')
  31.  

backends.py

Далі напишемо власний бекенд аутентифікації, який буде заміняти бекенд Django.

  1. # -*- coding: utf-8 -*-
  2.  
  3. from django.contrib.auth import get_user_model
  4. from django.utils import timezone
  5.  
  6.  
  7. class MyBackend:
  8.  
  9. def get_user(self, user_id):
  10. try:
  11. user = get_user_model().objects.get(pk=user_id)
  12. user.last_online = timezone.now() # При запиті користувача виконаємо оновленні дати і часу останнього відвідування
  13. user.save(update_fields=['last_online'])
  14. return user
  15. except get_user_model().DoesNotExist:
  16. return None
  17.  

settings.py

Не забуваємо зареєструвати наш додаток ...

  1. INSTALLED_APPS = [
  2. 'my_auth.apps.MyAuthConfig',
  3. ...
  4. ]

... і додати бекенд аутентифікації

  1. AUTHENTICATION_BACKENDS = (
  2. 'my_auth.backends.MyBackend',
  3. 'django.contrib.auth.backends.ModelBackend',
  4. )

Висновок

Тепер при кожному запиті користувача до сайту у вашого полььзователя буде оновлюватися інформація про його останньому відвідуванні.

І ви зможете в шаблоні робити у пользовтелей в контенті, чи знаходиться він онлайн чи ні. Наприклад так, щоб додати значок Online у аватарки

  1. {% if user.is_online %}
  2. <div class="online"></div>
  3. {% endif %}

або так відображати інформацію про його онлацн статус.

  1. <span class="text-muted">{{ user_profile.get_online_info }}</span>

Можливий недолік даного методу полягає в тому, що користувач може відкрити сторінку і нічого не робити цілу годину на ній.
Але в моєму випадку це не є проблемою, оскільки я кожну хвилину викликаю перевірку статусної інформації у аутентифицированного користувача, таким чином, поки сторінка користувача залишається відкритою, кожну хвилину оновлюється його статус онлайн.

Вам це подобається? Поділіться в соціальних мережах!

Владислав Меленчук
  • 03 червня 2020 р. 15:43

Доброго дня. А можно реализовать с помощью этой фичи вывод блок "Пользователи онлайн" и окрашивать взависимости от группы юзера? Как в скрине ниже.
Screenshot_36.png Screenshot_36.png

Добрый день.
Конечно, нужно будет только шаблонный тег написать, который будет забирать всех пользователей онлайн, проверяя группу текущего пользователя.
У меня пользователи онлайн на отдельной странице в разделе пользователей.

Anton
  • 21 липня 2020 р. 17:47
  • (відредаговано)


Здравствуйте. как внедрить эту систему если у меня уже есть авторизация и всё с ней связанное? что бы это не перекрывало весь мой код?

Evgenii Legotckoi
  • 22 липня 2020 р. 01:49

Добрый день. Вам нужно скопировать в класс AdvUser содержимое класса User из статьи. Полностью весь класс копировать не требуется.
Полагаю, что в файле settings.py у вас уже переопределён класс пользователя на ваш в переменной AUTH_USER_MODEL

Остальной код написан относительно универсально, достаточно лишь добавить файлы с содержимым как в статье в ваше существующее app аутентификации.

Также не забудьте создать миграцию и применить её, чтобы добавилось новое поле в таблице пользователя.

Anton
  • 22 липня 2020 р. 11:21

Большое спасибо!

Anton
  • 22 липня 2020 р. 13:50

И так , не все получилось. Сделал как вы говорили. И у меня как и раньше перебивает мою авторизацию. При входе в admin Вот эта ошибка

после чего я удалил из settings поле 'manager_school.backends.MyBackend'. Меня пустило. Но дело в том что(понятно что оно без этой строчки работать не будет) если я дату указываю в поле админки то мне выдаёт что человек онлайн.И даже если я выйду он всё-равно online. без этой строчки не отдаботает

И я понимаю что дело в 'manager_school.backends.MyBackend' этой строчке . но Как же мне сделать что бы он не заменял собой все настройки авторизации...?

Evgenii Legotckoi
  • 22 липня 2020 р. 14:01

Длительность сохранения статуса онлайн от последнего запроса страницы составляет 15 минут.

  1. def is_online(self):
  2. if self.last_online:
  3. return (timezone.now() - self.last_online) < timezone.timedelta(minutes=15) # здесь выполняется эта проверка на 15 минутный интервал
  4. return False

Можете уменьшить на одну минуту и посмотреть результат. Вполне возможно, что у вас всё работает.

Данная система статуса онлайн не совершенна, поскольку требует периодического обращения к серверу. Чтобы обновлять время последнего статуса онлайн. У меня эта проблема решается за счёт обращения к серверу раз в одну минуту для проверки уведомлений через JavaScript. Остальные разработчики решают эту часть своими способоами, как у них это выходит.

Evgenii Legotckoi
  • 22 липня 2020 р. 14:15

Да ещё.

Покажите код ваших бэкендов аутентификации, а также как скопировали код класса MyBackend

Помимо прочего, какую версию Джанго используете?

P/S/ Используйте диалог вставки программного кода, чтобы был код с разметкой. Не надо вставлять изображения кода, на любьом форуме это считается плохим тоном.

P/P/S/ И я просил на форуме задать вопрос, а не в комментариях к статье ))

Anton
  • 22 липня 2020 р. 14:18

Спасибо за ответ. уменьшил время до 14 минут и до 10 минут , ошибка осталась та же 'MyBackend' object has no attribute 'authenticate'

Anton
  • 22 липня 2020 р. 14:26
  • (відредаговано)

MyBackend

  1. from django.contrib.auth import get_user_model
  2. from django.utils import timezone
  3.  
  4.  
  5. class MyBackend:
  6. def get_user(self, user_id):
  7. try:
  8. user = get_user_model().objects.get(pk=user_id)
  9. user.last_online = timezone.now() # При запросе пользователя выполним обновлении даты и времени последнего посещения
  10. user.save(update_fields=['last_online'])
  11. return user
  12. except get_user_model().DoesNotExist:
  13. return None

Версия Django 3.0.8
бэк аутентификации

  1. AUTHENTICATION_BACKENDS = (
  2. 'manager_school.backends.MyBackend',
  3. 'django.contrib.auth.backends.ModelBackend',
  4. )
Evgenii Legotckoi
  • 22 липня 2020 р. 14:28
  • (відредаговано)

Извиняюсь. Время здесь влияние не оказывает, это я ответил по поводу сохранения статуса онлайн.

Evgenii Legotckoi
  • 22 липня 2020 р. 14:29

Надо подумать. Я писал этот статус онлайн для Django 2. Вполне возможно, что для третьей версии там используется несколько иной механизм бэкендов.
Смогу посмотреть это только после работы уже.

Anton
  • 22 липня 2020 р. 17:11

Я нашел ответ на ошибку

  1. from django.contrib.auth.backends import BaseBackend
  2.  
  3. class MyBackend(BaseBackend):
  4. ....
Evgenii Legotckoi
  • 22 липня 2020 р. 17:18

Ясно, значит для Django 3 обязательно наследование от BaseBackend при создание бэкенда аутентификации.
Скорее всего отсутствовал метод authenticate, который присутсвует при наследовании от BaseBackend

NSProject
  • 18 лютого 2022 р. 22:33
  • (відредаговано)

Хорошая статья для создания собственной батарейки для Django. Правда под каждую версию фреймворка приходится допиливать код бэкенда. Так как для версии Django 4 если наследовать бэкенд от BaseBackend то обновление статуса не происходит. Он просто проходит мимо метода get_user. По этому наследование лучше делать от ModelBackend и всё начинает работать.
Остальной весь код рабочий! Проверено на Django 4.

V
  • 02 квітня 2022 р. 23:09

Сделал всё как описано но при создании миграций возникает ошибка:
SystemCheckError: System check identified some issues:

ERRORS:
auth.User.groups: (fields.E304) Reverse accessor for 'auth.User.groups' clashes with reverse accessor for 'users.User.groups'.
HINT: Add or change a related_name argument to the definition for 'auth.User.groups' or 'users.User.groups'.
auth.User.user_permissions: (fields.E304) Reverse accessor for 'auth.User.user_permissions' clashes with reverse accessor for 'users.User.user_permissions'.
HINT: Add or change a related_name argument to the definition for 'auth.User.user_permissions' or 'users.User.user_permissions'.
users.User.groups: (fields.E304) Reverse accessor for 'users.User.groups' clashes with reverse accessor for 'auth.User.groups'.
HINT: Add or change a related_name argument to the definition for 'users.User.groups' or 'auth.User.groups'.
users.User.user_permissions: (fields.E304) Reverse accessor for 'users.User.user_permissions' clashes with reverse accessor for 'auth.User.user_permissions'.
HINT: Add or change a related_name argument to the definition for 'users.User.user_permissions' or 'auth.User.user_permissions'.

NSProject
  • 03 квітня 2022 р. 00:03
  • (відредаговано)

А вы создали в settings.py?

  1. AUTH_USER_MODEL = 'you_app.User'

А так же во всех моделях где есть внешний ключ на юзера нужно ставить

  1. settings.AUTH_USER_MODEL
NSProject
  • 26 серпня 2022 р. 20:40

Евгений у меня вопрос. В общем и целом оно всё работает. Но есть маленький затык. Это так сказать дело в том что при переключении языка "Last visit" переводится а вот то что отдаёт функция "naturaltime" нет. Есть какая то возможность это исправить? Или нужно переписать код этой самой функции?

Evgenii Legotckoi
  • 02 вересня 2022 р. 15:12

Я обычно использую localtime и всё работает.

  1. {% load tz %}
  2.  
  3. {% localtime on %}
  4. {{ value }}
  5. {% endlocaltime %}
  6.  
  7. {% localtime off %}
  8. {{ value }}
  9. {% endlocaltime %}

Коментарі

Only authorized users can post comments.
Please, Log in or Sign up
  • Останні коментарі
  • Evgenii Legotckoi
    16 квітня 2025 р. 17:08
    Благодарю за отзыв. И вам желаю всяческих успехов!
  • IscanderChe
    12 квітня 2025 р. 17:12
    Добрый день. Спасибо Вам за этот проект и отдельно за ответы на форуме, которые мне очень помогли в некоммерческих пет-проектах. Профессиональным программистом я так и не стал, но узнал мно…
  • AK
    01 квітня 2025 р. 11:41
    Добрый день. В данный момент работаю над проектом, где необходимо выводить звук из программы в определенное аудиоустройство (колонки, наушники, виртуальный кабель и т.д). Пишу на Qt5.12.12 поско…
  • Evgenii Legotckoi
    09 березня 2025 р. 21:02
    К сожалению, я этого подсказать не могу, поскольку у меня нет необходимости в обходе блокировок и т.д. Поэтому я и не задавался решением этой проблемы. Ну выглядит так, что вам действитель…
  • VP
    09 березня 2025 р. 16:14
    Здравствуйте! Я устанавливал Qt6 из исходников а также Qt Creator по отдельности. Все компоненты, связанные с разработкой для Android, установлены. Кроме одного... Когда пытаюсь скомпилиров…