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

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


Один из способов добавления статуса пользователей основывается на использовании 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 %}

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

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

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