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

22

我只是在Django中使用管理站点。我有两个Django信号(pre_save和post_save)。我想获得当前用户的用户名。我该怎么做?似乎我不能发送请求,或者我没有理解它。

谢谢

6个回答

38

我不想去处理线程本地状态,所以决定尝试另一种方法。据我所知,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实现中都不一定存在。


4
您先生,是一位传奇。优秀的解决方案,正如广告所述那样完美运作。 - Bosco
1
这种方法的优缺点是什么? - Mevin Babu
6
优点 - 能够发挥作用,实现更简单,避免线程本地变量(可能好也可能不好)。缺点 - 性能受影响(必须潜在地扫描每个信号的调用堆栈),无法通过测试运行 - 您需要添加额外的逻辑来从测试中提取请求对象。 - Ben
1
我已将其更改为reversed(inspect.stack()),因为在我的Django项目中的62次迭代中,get_response在第49次迭代中被找到,这告诉我们以相反的方向进行扫描应该更有利。 - Aleksei Khatkevich
1
将整个62项反转并扫描前10项的时间比扫描前49项。使用timeit进行一些测试。但我很惊讶差别如此之小。 - Olivier Pons
好的快速解决方案! - Harlin

12

如果您正在使用管理员网站,为什么不使用自定义模型管理呢?

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信号中获取用户请求的唯一方法吗? - Bun Suwanparsert
这是正确的答案,结合ModelAdmin.exclude来处理必填的预填充字段非常有用。 - chirale
这对于某些情况有效,但并非所有情况都适用,例如只能通过信号钩取m2m关系的更改。 - Ben

3

1
该解决方案依赖于线程本地变量。由于各种原因,这些被认为是“不好的”。https://dev59.com/DHA75IYBdhLWcg3wkJ71 - Aaron C. de Bruyn
仅对@AaronC.deBruyn所说的进行评论-他发布的链接实际上与他的陈述不符,并且普遍意见认为使用线程本地变量来实现此目的是可以接受的。 - Ben

2
我们可以使用中间件类来解决这个问题。 创建一个单例类,在其中存储用户变量。
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

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

logged_in = LoggedInUser()
user = logged_in.user

0
我会这样做,如果我不使用管理员网站,我还在寻找更好的解决方案。
@receiver(post_save, sender=FlowList)
def flow_list_post_save(sender, instance, created, **kwargs):
     import inspect
     request = None
     try:
        request = next((frame[0].f_locals['request'] for frame in inspect.stack() if frame[3] == 'get_response'), None)
     except StopIteration:
            pass

-1

想法正确,但参数的顺序是错误的。sender(发送信号的类)始终排在第一位。 - Daniel Roseman
已更正,谢谢...你所说的是Django标准,但是(就我使用的情况而言),在(实例,发送者)顺序中使用参数也可以工作(至少在我的代码中)。 - FallenAngel
15
uname = instance.username 要求你的模型有一个叫做 username 的字段,但这并不符合我的情况。 - Djanux
这里的“不起作用实例”指的是在调用信号之前创建的对象。 - Pradeep Sapkota

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接