Django: сигнал, коли користувач входить?


83

У моїй програмі Django мені потрібно почати виконувати кілька періодичних фонових завдань, коли користувач входить в систему, і припиняти їх, коли користувач виходить із системи, тому я шукаю елегантний спосіб

  1. отримувати повідомлення про вхід / вихід користувача
  2. запитувати статус входу користувача

З моєї точки зору, ідеальним рішенням було б

  1. сигнал, надісланий кожним django.contrib.auth.views.loginі... views.logout
  2. метод 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 User.

Хтось може подумати про альтернативні підходи?


4
Будь ласка, розгляньте вибір нової відповіді. Наразі прийнятий - дуже поганий вибір у світлі сигналу, доданого в 1.3.
Bryson

1
Ти маєш рацію; змінив прийняту відповідь.
ssc

Відповіді:


153

Ви можете використовувати такий сигнал (я вклав свій у models.py)

from django.contrib.auth.signals import user_logged_in


def do_stuff(sender, user, request, **kwargs):
    whatever...

user_logged_in.connect(do_stuff)

Див. Документи django: https://docs.djangoproject.com/en/dev/ref/contrib/auth/#module-django.contrib.auth.signals та тут http://docs.djangoproject.com/en/dev/ теми / сигнали /


8
Тепер, коли Django 1.3 пропонує ці сигнали, це набагато краще рішення, ніж перенесення виклику на вхід / вихід. Це також означає, що якщо ви налаштуєте нові способи входу - наприклад, входи в Facebook / Twitter / OpenID - вони все одно працюватимуть.
Jordan Reiter

9
Замість того, щоб помістити це models.pyзамість цього, я пропоную помістити код signals.pyі автоматично імпортувати його у __init__.pyфайл модулів .
Даніель Соколовський

як використовувати цей сигнал для запуску javascript під час входу та виходу?
Ashish Gupta

16

На додаток до відповіді @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в свій каталог програми, додайте це до apps.py:

class AppNameConfig(AppConfig):
    ...
    def ready(self):
        import app_name.signals

13

Одним із варіантів може бути обернення власних подань входу / виходу 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.

Якщо у вас немає доступу до об’єкта запиту, то визначити, чи ввійшов поточний користувач, буде важко.

Редагувати:

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

По-перше, чому ви не можете отримати цю функціональність? Ну, ви не можете сказати різницю між тим, хто закриває веб-переглядач, або тим, хто проводить час на сторінці перед тим, як завантажити новий. Неможливо сказати через HTTP, коли хтось фактично залишає сайт або закриває браузер.

Отже, у вас є два варіанти, які не ідеальні:

  1. Використовуйте unloadподію Javascript, щоб вловлювати, коли користувач залишає сторінку. Вам доведеться написати дещо обережну логіку, щоб переконатися, що ви не виходите з користувача, коли він все ще переходить на ваш сайт.
  2. Запустіть сигнал виходу щоразу, коли користувач входить в систему, щоб бути впевненим. Також створіть завдання cron, яке досить часто виконується для очищення прострочених сеансів. Коли сеанс, що закінчився, видаляється, переконайтеся, що у користувача сеансу (якщо він не анонімний) немає більше активних сеансів, і в цьому випадку ви запускаєте сигнал виходу.

Ці рішення брудні та не ідеальні, але вони, на жаль, найкращі, що ви можете зробити.


Це все ще не стосується ситуації "більшість випадків користувачі просто залишають сторінку або закривають браузер".
Joel L

Дякую за вашу відповідь, звичайно, обгортання входу / виходу позбавляє мене від виправлення джерела, хоча це мав би бути сам. Однак невелика корекція: якщо сигнал надсилається в my_login до виклику входу, користувач все ще залишається анонімним у обробнику сигналу. Краще (не думайте, що це буде правильно відформатовано): def my_login (запит): response = login (запит) #fire a signal, or equivalent return response Крім того, я вже використовую is_authenticated, у мене просто було відчуття, що мені знадобиться більше того. Поки що, проте, мій новий фрагмент is_logged_in у моделі даних сидить там невикористаним.
ssc

Гарні точки навколо. Думаю, я насправді не is_logged_inдуже добре звернувся до цього матеріалу (вибачення там, мабуть, я не дуже добре прочитав пост), але я оновив відповідь, запропонувавши допомогу в цій галузі. . На жаль, це трохи неможлива проблема.
ShZ

3

швидким рішенням для цього буде, у _ _ 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....')

1

Єдиним надійним способом (який також визначає, коли користувач закрив браузер) є оновлення деякого last_requestполя кожного разу, коли користувач завантажує сторінку.

Ви також можете мати періодичний запит AJAX, який пінгує сервер кожні хвилини, якщо у користувача відкрита сторінка.

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


Це найкращий спосіб виміряти, чи ввійшов користувач в систему чи ні. Вам в основному доведеться вести список зареєстрованих користувачів, перевіряти, яким був останній доступ, і приймати рішення про час очікування. Якщо ви встановите час очікування приблизно на 10 хвилин, а потім також пропонуєте кожній веб-сторінці викликати запит Ajax кожні 5 хвилин або близько того, коли сторінка активна, це повинно підтримувати актуальний статус.
Jordan Reiter

1

Посилання на вихід, на відміну від того, що вони явно натискають кнопку (чого ніхто не робить), означає вибір кількості простоїв, що дорівнює "виходу з системи". phpMyAdmin використовує за замовчуванням 15 хвилин, деякі банківські сайти використовують лише 5 хвилин.

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


0

Груба ідея - для цього ви можете використовувати проміжне програмне забезпечення. Це проміжне програмне забезпечення може обробляти запити та сигнали про спрацьовування, коли запитується відповідна URL-адреса. Він також може обробляти відповіді та пожежний сигнал, коли дана дія насправді вдається.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.