在Django中获取当前用户登录信号

Posted

技术标签:

【中文标题】在Django中获取当前用户登录信号【英文标题】:Get current user log in signal in Django 【发布时间】:2011-06-10 22:37:20 【问题描述】:

我只是在 Django 中使用管理站点。我有 2 个 Django 信号(pre_save 和 post_save)。我想拥有当前用户的用户名。我该怎么做?好像不能发请求或者没看懂。

谢谢

【问题讨论】:

【参考方案1】:

在两个信号中,信号发送三个参数,

发件人 实例 使用

您需要的是正在修改的 Instant...

def signal_catcher(sender, instance, **kwargs):
    uname = instance.username

http://docs.djangoproject.com/en/dev/ref/signals/#pre-save

【讨论】:

正确的想法,但论点是错误的方式。 sender(发送信号的类)永远是第一位的。 已更正,谢谢...您所说的是 djanog 标准,但是(就我使用它而言)使用(实例,发件人)顺序中的参数也可以(至少在我的代码中) ... uname = instance.username 要求在您的模型中有一个名为 username 的字段?...这不是我的情况... 这不起作用实例意味着在调用信号之前创建的对象。【参考方案2】:

您可以使用中间件来存储当前用户:http://djangosnippets.org/snippets/2179/

那么你就可以得到get_current_user()的用户

【讨论】:

该解决方案依赖于线程局部变量。由于各种原因,这些被认为是“坏的”。 ***.com/questions/3227180/… 只是对@AaronC.deBruyn 所说的话的评论 - 他发布的链接实际上不同意他的声明,并且普遍认为为此目的使用线程本地人是可以接受的 .【参考方案3】:

我们可以使用中间件类来解决这个问题。 在将存储用户变量的位置创建单例类。

class Singleton(type):
    '''
        Singleton pattern requires for GetUser class
    '''
    def __init__(cls, name, bases, dicts):
        cls.instance = None

    def __call__(cls, *args, **kwargs):
        if cls.instance is None:
            cls.instance = super(Singleton, cls).__call__(*args, **kwargs)
        return cls.instance 


class NotLoggedInUserException(Exception):
    '''
    '''
    def __init__(self, val='No users have been logged in'):
        self.val = val
        super(NotLoggedInUser, self).__init__()

    def __str__(self):
        return self.val

class LoggedInUser(object):
    __metaclass__ = Singleton

    user = None

    def set_user(self, request):
        if request.user.is_authenticated():
            self.user = request.user

    @property
    def current_user(self):
        '''
            Return current user or raise Exception
        '''
        if self.user is None:
            raise NotLoggedInUserException()
        return self.user

    @property
    def have_user(self):
    return not user is None

创建自己的中间件类,为 LoggedInUser 实例设置用户,并在 settings.py 中的 'django.contrib.auth.middleware.AuthenticationMiddleware' 之后插入中间件

from useranytimeaccess import LoggedInUser
class LoggedInUserMiddleware(object):
    '''
        Insert this middleware after django.contrib.auth.middleware.AuthenticationMiddleware
    '''
    def process_request(self, request):
        '''
            Returned None for continue request
        '''
        logged_in_user = LoggedInUser()
        logged_in_user.set_user(request)
        return None

In 信号导入 LoggedInUser 类并获取当前用户

logged_in = LoggedInUser()
user = logged_in.user

【讨论】:

【参考方案4】:

如果您使用的是管理站点,为什么不使用自定义模型管理员

class MyModelAdmin( admin.ModelAdmin ):
    def save_model( self, request, obj, form, change ):
        #pre save stuff here
        obj.save()
        #post save stuff here



admin.site.register( MyModel, MyModelAdmin )

信号是每次保存对象时都会触发的东西,无论它是由管理员完成还是由与请求无关且实际上不适合基于请求执行的某个进程完成行动

【讨论】:

2015 年 2 月。这仍然是在 Django 信号中获取用户请求的唯一方法吗? 这是正确的答案,加上ModelAdmin.exclude 对必填字段非常有用。 这适用于某些但不是所有情况,例如对m2m 关系的更改只能通过信号挂钩。【参考方案5】:

由于不愿搞乱线程本地状态,我决定尝试不同的方法。据我所知,post_savepre_save 信号处理程序在调用save() 的线程中被同步调用。如果我们处于正常的请求处理循环中,那么我们可以直接在堆栈中查找请求对象作为某个地方的局部变量。例如

from django.db.models.signals import pre_save
from django.dispatch import receiver

@receiver(pre_save)
def my_callback(sender, **kwargs):
    import inspect
    for frame_record in inspect.stack():
        if frame_record[3]=='get_response':
            request = frame_record[0].f_locals['request']
            break
    else:
        request = None
    ...

如果有当前请求,您可以从中获取user 属性。

注意:就像 inspect 模块文档中所说的,

这个函数依赖于解释器中的 Python 栈帧支持,这不是 保证存在于 Python 的所有实现中。

【讨论】:

你先生,是个传奇。出色的解决方案,效果与宣传的一样。 这种方法的优缺点? 优点 - 有效,实现更简单,避免线程局部变量(可能是也可能不是坏事)缺点 - 性能下降(有扫描每个信号的调用堆栈),不能通过测试工作 - 你需要添加额外的逻辑来从你的测试中提取请求对象 我已将其更改为 reversed(inspect.stack()) cos 从我的 Django 项目中的整体 62 次迭代中找到 get_response 在第 49 次迭代中找到,这告诉我们反向扫描应该更有利可图。 反转整个 62 个项目 + 先扫描 ~10 个的时间比先扫描 ~49 个要。用 timeit 做一些测试。但我很惊讶差异如此之小。【参考方案6】:

无需 hack:在用户登录/注销时使用信号来记住谁登录了,对于 Django 测试框架,只要您连接并调用信号,这也可以:

current_user = None

@receiver(user_logged_in, sender=User)
def user_logged_in(sender, request, user, **kwargs):
    global current_user
    current_user = user

@receiver(user_logged_out, sender=User)
def user_logged_out(sender, request, user, **kwargs):
    global current_user
    current_user = None

@receiver(post_save, sender=StatusHistory)
def status_history_post_save(sender, instance: StatusHistory,
                             created, **kwargs):
    global current_user
    if current_user is None or instance is None or instance.shipment is None:
        return
    # ok if we here -> user is connected, keep on:
    # .........
    # blabla
    # .........

【讨论】:

仅当您的网站一次只有一个用户登录时才有效。

以上是关于在Django中获取当前用户登录信号的主要内容,如果未能解决你的问题,请参考以下文章

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

如何从 django-oauth-toolkit 令牌获取当前登录用户?

如何使用当前登录用户作为 Django DetailView 的 PK?

如何在python中找到用户的登录位置

使用 django-allauth 登录信号将用户重定向到另一个 url

Django:在模型保存中获取当前用户