Django: сигнал, когда пользователь входит в систему?

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

  • получить уведомление о входе/выходе пользователя
  • запрашивать статус входа пользователя

С моей точки зрения, идеальным решением было бы

  • сигнал, отправленный каждым django.contrib.auth.views.login и ... views.logout
  • метод django.contrib.auth.models.User.is_logged_in(), аналогичный ... User.is_active() или ... User.is_authenticated()

В Django 1.1.1 этого нет, и я не хочу исправлять исходный код и добавлять его (не знаю, как это сделать, так или иначе).

В качестве временного решения я добавил логическое поле is_logged_in в модель UserProfile, которая по умолчанию очищается, устанавливается при первом обращении пользователя к целевой странице (определяется LOGIN_REDIRECT_URL = '/') и запрашивается в последующих Запросы. Я добавил его в UserProfile, поэтому мне не нужно выводить и настраивать встроенную модель пользователя только для этой цели.

Мне не нравится это решение. Если пользователь явно нажимает кнопку выхода, я могу очистить флаг, но большую часть времени пользователи просто покидают страницу или закрывают браузер; очистка флага в этих случаях не кажется мне прямой. Кроме того (что, скорее, ясность четности данных модели), is_logged_in не принадлежит в UserProfile, а в модели User.

Может ли кто-нибудь подумать об альтернативных подходах?

Ответ 2

Один из вариантов может состоять в том, чтобы объединить виды входа/выхода в Django с вашим собственным. Например:

from django.contrib.auth.views import login, logout

def my_login(request, *args, **kwargs):
    response = login(request, *args, **kwargs)
    #fire a signal, or equivalent
    return response

def my_logout(request, *args, **kwargs):
    #fire a signal, or equivalent
    return logout(request, *args, **kwargs)

Затем вы используете эти представления в своем коде, а не в Django и voila.

Что касается запроса статуса входа в систему, это довольно просто, если у вас есть доступ к объекту запроса; просто проверьте атрибут пользователя запроса, чтобы узнать, являются ли они зарегистрированным пользователем или анонимным пользователем, а также бинго. Чтобы процитировать документацию Django:

if request.user.is_authenticated():
    # Do something for logged-in users.
else:
    # Do something for anonymous users.

Если у вас нет доступа к объекту запроса, определение того, будет ли текущий пользователь зарегистрирован, будет затруднительным.

Edit:

К сожалению, вы никогда не сможете получить функциональность User.is_logged_in() - это ограничение протокола HTTP. Однако, если вы сделаете несколько предположений, вы сможете приблизиться к тому, что хотите.

Во-первых, почему вы не можете получить эту функциональность? Ну, вы не можете сказать разницу между тем, кто закрывает браузер, или кто-то тратит время на странице, прежде чем выберет новый. Невозможно передать HTTP-сообщение, когда кто-то действительно покидает сайт или закрывает браузер.

Итак, у вас есть два варианта, которые не идеальны:

  • Использовать Javascript unload, чтобы поймать, когда пользователь покидает страницу. Вы должны написать некоторую осторожную логику, чтобы убедиться, что вы не выходите из системы, когда пользователь все еще перемещает ваш сайт.
  • Огоньте сигнал выхода, когда пользователь входит в систему, чтобы быть уверенным. Также создайте задание cron, которое выполняется довольно часто, чтобы очистить сеансы с истекшим сроком действия - при удалении истекшего сеанса убедитесь, что пользователь сеанса (если он не анонимный) не имеет более активных сеансов, и в этом случае вы запускаете сигнал выхода из системы.

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

Ответ 3

В дополнение к @PhoebeB ответ: вы также можете использовать декоратор @receiver следующим образом:

from django.contrib.auth.signals import user_logged_in
from django.dispatch import receiver

@receiver(user_logged_in)
def post_login(sender, user, request, **kwargs):
    ...do your stuff..`

И если вы поместите его в signals.py в свой каталог приложений, добавьте его в app.py:

def ready(self):
    import app_name.signals`

Ответ 4

Единственный надежный способ (который также обнаруживает, когда пользователь закрыл браузер) заключается в обновлении некоторого поля last_request каждый раз, когда пользователь загружает страницу.

У вас также может быть периодический запрос AJAX, который связывает сервер каждые x минут, если у пользователя открыта страница.

Затем у вас есть одно фоновое задание, которое получает список последних пользователей, создает задания для них и очищает задания для пользователей, не присутствующих в этом списке.

Ответ 5

Вывод выхода из системы, в отличие от того, что они явно нажимают кнопку (что никто не делает), означает выбор количества простоя, которое приравнивается к "выходу из системы". phpMyAdmin использует по умолчанию 15 минут, некоторые банковские сайты используют всего 5 минут.

Самый простой способ реализовать это - изменить время жизни cookie. Вы можете сделать это для всего своего сайта, указав settings.SESSION_COOKIE_AGE. В качестве альтернативы вы можете изменить его для каждого пользователя (на основе некоторого произвольного набора критериев), используя HttpResponse.setcookie(). Вы можете централизовать этот код, создав собственную версию render_to_response() и установив для каждого ответа время жизни.

Ответ 6

Грубая идея - для этого вы можете использовать промежуточное ПО. Это промежуточное программное обеспечение может обрабатывать запросы и сигнал пожара, когда запрашивается соответствующий URL-адрес. Он также может обрабатывать ответы и сигнал огня, когда данное действие действительно выполняется.

Ответ 7

быстрое решение для этого было бы в _ _ init _ _.py вашего приложения разместить следующий код:

from django.contrib.auth.signals import user_logged_in
from django.dispatch import receiver


@receiver(user_logged_in)
def on_login(sender, user, request, **kwargs):
    print('User just logged in....')

Ответ 8

Вы можете использовать следующее:

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

@login_required
def user_logout(request):
    logout(request)
    user_logged_out()
    return redirect('post_list')