Evgenii Legotckoi
Evgenii Legotckoi02 вересня 2019 р. 07:31

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

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

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

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


my_auth

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

python manage.py startapp my_auth

models.py

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

# -*- coding: utf-8 -*-

from django.contrib.auth.models import AbstractUser
from django.contrib.humanize.templatetags.humanize import naturaltime
from django.db import models
from django.utils import timezone
from django.utils.translation import ugettext_lazy as _


class User(AbstractUser):

    last_online = models.DateTimeField(blank=True, null=True)

    # В даному методі перевірка, що дата останнього відвідування не старше 15 хвилин
    def is_online(self):
        if self.last_online:
            return (timezone.now() - self.last_online) < timezone.timedelta(minutes=15)
        return False

    # Якщо користувач відвідував сайт не більше 15 хвилин назад,
    def get_online_info(self):
        if self.is_online():
            # то повертаємо інформацію, що він онлайн
            return _('Online')
        if self.last_online:
            # інакше пишемо повідомлення про останньому відвідуванні
            return _('Last visit {}').format(naturaltime(self.last_online))
            # Якщо ви тільки недавно додали інформацію про відвідування користувачем сайту
            # То для деяких користувачів інфомація про відвідування може і не бути, повернемо інформацію, що останній візит невідомо
        return _('Unknown')

backends.py

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

# -*- coding: utf-8 -*-

from django.contrib.auth import get_user_model
from django.utils import timezone


class MyBackend:

    def get_user(self, user_id):
        try:
            user = get_user_model().objects.get(pk=user_id)
            user.last_online = timezone.now()  # При запиті користувача виконаємо оновленні дати і часу останнього відвідування
            user.save(update_fields=['last_online'])
            return user
        except get_user_model().DoesNotExist:
            return None

settings.py

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

INSTALLED_APPS = [
    'my_auth.apps.MyAuthConfig',
    ...
]

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

AUTHENTICATION_BACKENDS = (
    'my_auth.backends.MyBackend',
    'django.contrib.auth.backends.ModelBackend',
)

Висновок

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

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

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

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

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

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

Рекомендуємо хостинг TIMEWEB
Рекомендуємо хостинг TIMEWEB
Стабільний хостинг, на якому розміщується соціальна мережа EVILEG. Для проектів на Django радимо VDS хостинг.

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

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

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

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

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


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

Evgenii Legotckoi
  • 21 липня 2020 р. 15:49

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Да ещё.

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

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

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

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

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

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

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

MyBackend

from django.contrib.auth import get_user_model
from django.utils import timezone


class MyBackend:
    def get_user(self, user_id):
        try:
            user = get_user_model().objects.get(pk=user_id)
            user.last_online = timezone.now()  # При запросе пользователя выполним обновлении даты и времени последнего посещения
            user.save(update_fields=['last_online'])
            return user
        except get_user_model().DoesNotExist:
            return None

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

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

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

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

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

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

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

from django.contrib.auth.backends import BaseBackend

class MyBackend(BaseBackend):
    ....
Evgenii Legotckoi
  • 22 липня 2020 р. 07:18

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

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

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

V
  • 02 квітня 2022 р. 13: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
  • 02 квітня 2022 р. 14:03
  • (відредаговано)

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

AUTH_USER_MODEL = 'you_app.User'

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

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

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

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

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

{% load tz %}

{% localtime on %}
    {{ value }}
{% endlocaltime %}

{% localtime off %}
    {{ value }}
{% endlocaltime %}

Коментарі

Only authorized users can post comments.
Please, Log in or Sign up
Г

C++ - Тест 001. Первая программа и типы данных

  • Результат:66бали,
  • Рейтинг балів-1
t

C++ - Тест 001. Первая программа и типы данных

  • Результат:33бали,
  • Рейтинг балів-10
t

Qt - Тест 001. Сигналы и слоты

  • Результат:52бали,
  • Рейтинг балів-4
Останні коментарі
G
GoattRock03 вересня 2024 р. 23:50
Як скопіювати файли в Linux Задумывались когда-нибудь о том, как мы привыкли доверять свои вещи службам грузоперевозок? Сейчас такие услуги стали неотъемлемой частью нашей жизни, особенно когда речь идет о переездах между …
ВР
Влад Русоков02 серпня 2024 р. 11:47
Як скопіювати файли в Linux Screenshot_20240802-065123.png
d
dblas505 липня 2024 р. 21:02
QML - Урок 016. База даних SQLite та робота з нею в QML Qt Здравствуйте, возникает такая проблема (я новичок): ApplicationWindow неизвестный элемент. (М300) для TextField и Button аналогично. Могу предположить, что из-за более новой верси…
k
kmssr09 лютого 2024 р. 05:43
Qt Linux - Урок 001. Автозапуск програми Qt під Linux как сделать автозапуск для флэтпака, который не даёт создавать файлы в ~/.config - вот это вопрос ))
АК
Анатолий Кононенко05 лютого 2024 р. 12:50
Qt WinAPI - Урок 007. Робота з ICMP Ping в Qt Без строки #include <QRegularExpressionValidator> в заголовочном файле не работает валидатор.
Тепер обговоріть на форумі
Evgenii Legotckoi
Evgenii Legotckoi25 червня 2024 р. 01:11
добавить qlineseries в функции Я тут. Работы оень много. Отправил его в бан.
F
Fynjy22 липня 2024 р. 14:15
при создании qml проекта Kits есть но недоступны для выбора Поставил Qt Creator 11.0.2. Qt 6.4.3 При создании проекта Qml не могу выбрать Kits, они все недоступны, хотя настроены и при создании обычного Qt Widget приложения их можно выбрать. В чем может …
BlinCT
BlinCT25 червня 2024 р. 11:00
Нарисовать кривую в qml Всем привет. Имеется Лист листов с тосками, точки получаны интерполяцией Лагранжа. Вопрос, как этими точками нарисовать кривую? ChartView отпадает сразу, в qt6.7 появился новый элемент…
BlinCT
BlinCT05 травня 2024 р. 15:46
Написать свой GraphsView Всем привет. В Qt есть давольно старый обьект дял работы с графиками ChartsView и есть в 6.7 новый но очень сырой и со слабым функционалом GraphsView. По этой причине я хочу написать х…
Evgenii Legotckoi
Evgenii Legotckoi03 травня 2024 р. 00:07
Мобильное приложение на C++Qt и бэкенд к нему на Django Rest Framework Добрый день. По моему мнению - да, но то, что будет касаться вызовов к функционалу Андроида, может создать огромные трудности.

Слідкуйте за нами в соціальних мережах