Django:用户登录时发出信号?

时间:2010-01-02 03:19:50

标签: python django login signals

在我的Django应用程序中,我需要在用户登录时开始运行一些定期后台作业,并在用户注销时停止运行它们,所以我正在寻找一种优雅的方式

  1. 收到用户登录/注销
  2. 的通知
  3. 查询用户登录状态
  4. 从我的角度来看,理想的解决方案是

    1. 每个django.contrib.auth.views.login... views.logout
    2. 发送的信号
    3. 方法django.contrib.auth.models.User.is_logged_in(),类似于... User.is_active()... User.is_authenticated()
    4. Django 1.1.1没有那个,我不愿修补源并添加它(不知道怎么做,无论如何)。

      作为临时解决方案,我在UserProfile模型中添加了is_logged_in布尔字段,默认情况下已清除,在用户第一次点击登录页面时设置(由LOGIN_REDIRECT_URL = '/'定义)和在随后的请求中查询。我将它添加到UserProfile,因此我不必为此目的派生和定制内置用户模型。

      我不喜欢这个解决方案。如果用户明确点击了注销按钮,我可以清除该标志,但大多数时候,用户只需离开页面或关闭浏览器;在这些情况下清除旗帜对我来说似乎不太直接。除此之外(相反,数据模型的清晰度仍然是挑剔),is_logged_in不属于UserProfile,而属于User模型。

      有人能想到其他方法吗?

7 个答案:

答案 0 :(得分:134)

你可以使用这样的信号(我把我放在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 docs:https://docs.djangoproject.com/en/dev/ref/contrib/auth/#module-django.contrib.auth.signalshttp://docs.djangoproject.com/en/dev/topics/signals/

答案 1 :(得分: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中使用这些视图,瞧。

关于查询登录状态,如果您有权访问请求对象,则非常简单;只需检查请求的用户属性,看看他们是注册用户还是匿名用户,以及宾果游戏。引用Django documentation

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

如果您无权访问请求对象,那么确定当前用户是否已登录将很困难。

修改

不幸的是,您永远无法获得User.is_logged_in()功能 - 这是HTTP协议的限制。但是,如果你做了一些假设,你可能会接近你想要的。

首先,为什么你不能获得这个功能?好吧,你无法区分关闭浏览器的人或者在获取新浏览器之前在页面上花费一些时间的人之间的区别。当有人实际离开网站或关闭浏览器时,无法通过HTTP判断。

所以你有两个不完美的选择:

  1. 使用Javascript的unload事件来捕获用户离开页面的时间。但是,您必须编写一些谨慎的逻辑,以确保在用户仍在浏览您的网站时未注销用户。
  2. 每当用户登录时触发注销信号,只是为了确定。还要创建一个cron作业,该作业经常运行以清除过期的会话 - 当删除过期的会话时,检查会话的用户(如果它不是匿名的)没有更多的活动会话,在这种情况下,您将触发注销信号。 / LI>

    这些解决方案很麻烦且不太理想,但不幸的是,它们是你能做的最好的。

答案 2 :(得分:7)

除了@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..`

如果您将其放入app dir中的signals.py,请将其添加到app.py

def ready(self):
    import app_name.signals`

答案 3 :(得分:1)

唯一可靠的方法(也检测用户何时关闭浏览器)是每次用户加载页面时更新一些last_request字段。

如果用户打开了一个页面,您还可以定期向AJAX请求每隔x分钟ping一次服务器。

然后有一个后台作业,它获取最近用户的列表,为他们创建作业,并清除该列表中不存在的用户的作业。

答案 4 :(得分:1)

推断注销,而不是明确单击按钮(没有人这样做),意味着选择等于“注销”的空闲时间。 phpMyAdmin使用默认值15分钟,一些银行网站只使用5分钟。

实现这一目标的最简单方法是更改​​cookie生存期。您可以通过指定settings.SESSION_COOKIE_AGE为整个网站执行此操作。或者,您可以使用HttpResponse.setcookie()基于每个用户(基于一些任意标准)更改它。您可以通过创建自己的render_to_response()版本并使其设置每个响应的生命周期来集中此代码。

答案 5 :(得分:0)

粗略的想法 - 您可以使用中间件。当请求相关URL时,此中间件可以处理请求和触发信号。当给定的动作实际成功时,它还可以处理响应和发出信号。

答案 6 :(得分:0)

快速解决此问题的方法是,在您应用的_ _ 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....')