Django База [2023]: Функционал статуса пользователя (онлайн/оффлайн) в Django с кэшированием #40
Django

Django База [2023]: Функционал статуса пользователя (онлайн/оффлайн) в Django с кэшированием #40

Razilator

Один из способов добавления статуса пользователей основывается на использовании Middleware, который позволяет обрабатывать запросы перед тем, как они будут обработаны во view. Middleware позволяет работать со всеми запросами, и поэтому он является отличным местом для реализации этого функционала.

Мы будем использовать также систему кэширования, чтобы наше Django приложение не нагружалось.

Добавим файловую систему кэширования

Пока мы обойдемся фаловой системой кэширования, а в следующих уроках мы познакомимся с Redis кэшированием, поэтому в конфигурационный файл settings.py добавим следующий код в любое место:

backend/settings.py
CACHES = {
    'default': {
        'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache',
        'LOCATION': (BASE_DIR / 'cache'),
    }
}

Создадим middleware с необходимым функционалом

Ниже приведен код middleware.py, который добавляет функционал статуса пользователей в приложение system:

modules/system/middleware.py
from django.contrib.auth.models import User
from django.core.cache import cache
from django.utils import timezone
from django.utils.deprecation import MiddlewareMixin


class ActiveUserMiddleware(MiddlewareMixin):

    def process_request(self, request):
        if request.user.is_authenticated and request.session.session_key:
            cache_key = f'last-seen-{request.user.id}'
            last_login = cache.get(cache_key)

            if not last_login:
                User.objects.filter(id=request.user.id).update(last_login=timezone.now())
                # Устанавливаем кэширование на 300 секунд с текущей датой по ключу last-seen-id-пользователя
                cache.set(cache_key, timezone.now(), 300)

Этот код представляет собой middleware-класс ActiveUserMiddleware, который используется для обновления статуса "онлайн" пользователя в Django с помощью кэширования.

Middleware-класс ActiveUserMiddleware определяет метод process_request(), который вызывается для каждого входящего запроса. В этом методе проверяется, авторизован ли пользователь, и имеет ли его сессия уникальный идентификатор session_key.

Если пользователь авторизован и имеет уникальный session_key, то обновляется его статус "последний раз в сети" с помощью метода cache.set() и сохраняется в кэше на время 300 секунд (можем установить любое значение).

Если в кэше нет записи для пользователя, то его последнее время входа в систему обновляется на текущее время с помощью метода User.objects.filter().update(), и время записывается в кэш.

Добавим этот middleware в конфигурационный файл проекта для его выполнения:

backend/settings.py
MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
    'debug_toolbar.middleware.DebugToolbarMiddleware',
    'modules.system.middleware.ActiveUserMiddleware',
]

Функция is_online() в модели профиля

В models.py, в модели Profile, добавляется метод is_online(), который проверяет, был ли пользователь онлайн в течение последних 5 минут:

modules/system/models.py
from django.db import models
from django.contrib.auth.models import User
from django.utils import timezone
from django.core.cache import cache


class Profile(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE)
    slug = models.SlugField(verbose_name='URL', max_length=255, blank=True, unique=True)
    
    # Другие поля...
    
    # Другие методы...
    
    def is_online(self):
        last_seen = cache.get(f'last-seen-{self.user.id}')
        if last_seen is not None and timezone.now() < last_seen + timezone.timedelta(seconds=300):
            return True
        return False

Вывод статуса в профиле пользователя

Переходим в наш html шаблон профиля пользователя (templates/system/profile_detail.html) и добавляем следующую строку в шаблон:

templates/system/profile_detail.html
{% extends 'main.html' %}

{% block content %}
<div class="card border-0">
        <div class="card-body">
            <div class="row">
                <div class="col-md-3">
                    <figure>
                        <img src="{{ profile.avatar.url }}" class="img-fluid rounded-0" alt="{{ profile }}">
                    </figure>
                </div>
                <div class="col-md-9">
                     <h5 class="card-title">
                        {{ profile }}
                    </h5>
                    <div class="card-text">
                        <ul>
                            <li>Никнейм: {{ profile.user.username }}</li>
                            {% if profile.user.get_full_name %} <li>Имя и фамилия: {{ profile.user.get_full_name }}</li> {% endif %}
                            <li>Заходил: {{ profile.user.last_login }} | {% if profile.is_online %}Онлайн{% else %}Не в сети{% endif %}</li>
                            <li>Дата рождения: {{ profile.birth_date }}</li>
                            <li>О себе: {{ profile.bio }}</li>
                        </ul>
                    {% if request.user == profile.user %} <a href="{% url 'profile_edit' %}" class="btn btn-sm btn-primary">Редактировать профиль</a> {% endif %}
                    </div>
                </div>
            </div>
        </div>
    </div>
{% endblock %}

Проверка работы статуса

При первом обращении на страницу мы получим 3 взаимодействия с кэшем, где увидим первое: получение ключа, установку ключа, получение ключа.
При первом обращении на страницу мы получим 3 взаимодействия с кэшем, где увидим первое: получение ключа, установку ключа, получение ключа.
Увидим, что пользователь онлайн (такой статус он будет иметь ещё 5 минут)
Увидим, что пользователь онлайн (такой статус он будет иметь ещё 5 минут)
При повторном запросе делается два запроса к кэшу,  запрос нового кэша будет выполнен лишь при запросе по истечению 300 секунд
При повторном запросе делается два запроса к кэшу, запрос нового кэша будет выполнен лишь при запросе по истечению 300 секунд
В случае, если мы не являемся авторизованным пользователем, не имея свой статус онлайна, мы делаем лишь одно обращение к кэшу только при посещении страницы пользователя, к тому же, мы подождали 5 минут, и пользователь не в сети
В случае, если мы не являемся авторизованным пользователем, не имея свой статус онлайна, мы делаем лишь одно обращение к кэшу только при посещении страницы пользователя, к тому же, мы подождали 5 минут, и пользователь не в сети
Сделаем любое действие с аккаунта, и убедимся, что с неавторизованного пользователя не делается лишних кэш запросов
Сделаем любое действие с аккаунта, и убедимся, что с неавторизованного пользователя не делается лишних кэш запросов
Обратите внимание, в рамках этого урока мы используем пока-что файловое кэширование
Обратите внимание, в рамках этого урока мы используем пока-что файловое кэширование

Таким образом мы создали оптимизированную систему статусов пользователей, используя систему кэширования. Вы можете использовать любую систему кэширования, рекомендуем: redis.

;