首页 文章

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

提问于
浏览
68

在我的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没有那个,我不愿修改源代码并添加它(不知道如何做到这一点,无论如何) .

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

虽然我不喜欢数据模型清晰度,但是 is_logged_in 不属于UserProfile,而是属于User模型 .

谁能想到替代方法?

8 回答

  • 7

    你可以使用这样的信号(我把它放在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/topics/signals/

  • 1

    一种选择可能是用自己的方式包装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,并且瞧 .

    关于查询登录状态,它是's pretty simple if you have access to the request object; simply check request'的用户属性,以查看他们是注册用户还是匿名用户,以及宾果游戏 . 引用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告知 .

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

    • 使用Javascript的 unload 事件来捕获用户离开页面的时间 . 但是,当用户仍在浏览您的网站时,请注销用户 .

    • 每当用户登录时触发注销信号,只是为了确定 . 还要创建一个cron作业,该作业经常运行以清除过期的会话 - 当删除过期的会话时,检查会话's user (if it's不是匿名的)没有更多的活动会话,在这种情况下,您将触发注销信号 .

    这些解决方案很混乱,并不理想,但不幸的是,它们是你能做的最好的 .

  • 1

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

    如果你把它放到app16的 signals.py 中,那么将其添加到 app.py

    def ready(self):
        import app_name.signals`
    
  • 0

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

    如果用户打开了页面,您还可以定期对每隔x分钟ping服务器的AJAX请求 .

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

  • 13

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

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

  • 0

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

  • 131

    一个快速的解决方案是,在你的应用程序的_ _ 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

    你可以使用喜欢这个:

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

相关问题