2010-01-02 4 views
62

В моем приложении Django, мне нужно, чтобы начать работать несколько периодических фоновых заданий, когда пользователь входит в систему и остановить запуская их, когда пользователь выходит из системы, так что я ищу элегантный способ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.

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

+4

Пожалуйста, обратите внимание при выборе нового ответа. Принимаемый в настоящее время один очень плохой выбор в свете сигнала, добавленного в 1.3. – Bryson

+1

Вы правы; изменил принятый ответ. – ssc

ответ

125

Вы можете использовать сигнал, как это (я кладу шахту в 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) 

См Джанго документы: https://docs.djangoproject.com/en/dev/ref/contrib/auth/#module-django.contrib.auth.signals и здесь http://docs.djangoproject.com/en/dev/topics/signals/

+8

Теперь, когда Django 1.3 предлагает эти сигналы, это гораздо лучшее решение, чем перенос вызова для входа/выхода из системы. Это также означает, что вы настраиваете новые способы входа в систему - например, логины Facebook/Twitter/OpenID - они все равно будут работать. –

+8

Вместо того, чтобы помещать это в 'models.py' вместо этого, я предлагаю разместить код в' signal.py' и автоматически импортировать его в файл '__init __. Py'. –

+0

Как использовать этот сигнал для запуска javascript при входе в систему и выходе из системы? –

12

Один из вариантов может заключаться в том, чтобы объединить виды входа/выхода из 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 documentation:

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

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

Edit:

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

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

Так у вас есть два варианта здесь, которые не совершенны:

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

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

+0

Это по-прежнему не относится к ситуации «большую часть времени, пользователи просто покидают страницу или закрывают браузер». –

+0

Спасибо за ваш ответ, конечно, обходной логин/выход из системы избавит меня от исправления источника, должен иметь хотя бы сам. Небольшая коррекция: если сигнал отправлен в my_login до того, как вызывается логин, пользователь по-прежнему анонимен в обработчике сигналов. Лучше (не думаю, что это будет отформатирован): Защиту мой_логин (запрос): ответ = Логин (запрос) #fire сигнала, или эквивалентный ответ возврат Кроме того, я использую is_authenticated уже, я просто было ощущение, что мне нужно больше, чем это. Пока, однако, моя новая вещь is_logged_in в модели данных там не используется. – ssc

+0

Хорошие точки со всех сторон. Наверное, я вообще совсем не обращался к материалу 'is_logged_in' вообще (извинения там, я думаю, я не очень хорошо читал сообщение), но я обновил ответ, чтобы предложить, какую помощь я могу в этой области. К сожалению, это немного нереальная проблема. – ShZ

0

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

1

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

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

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

+0

Это лучший способ определить, вошел ли пользователь в систему или нет. Вам в основном придется вести список зарегистрированных пользователей и проверять, что их последний доступ, и принять решение о тайм-ауте. Если вы установите тайм-аут на что-то вроде 10 минут, а затем также каждую страницу вызывают запрос Ajax каждые 5 минут или около того, пока страница активна, это должно поддерживать актуальность состояния. –

1

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

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

0

Вы можете использовать так:

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') 
2

В дополнение к @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` 
Смежные вопросы