美文网首页js css html
Django自带的Admin后台中如何获取当前登录用户

Django自带的Admin后台中如何获取当前登录用户

作者: 菩提老鹰 | 来源:发表于2023-04-11 14:13 被阅读0次

    需求背景

    在使用Django快速开发一个IT 电脑、显示器资产管理小系统的时候,遇到一个问题是,当变更资产设备(新增、修改、删除)的时候,能记录是谁在什么时间进行的变更。

    确认的是肯定是登录状态,但是在使用Django的signal中获取不到当前登录的用户

    问题演示

    1、定义资产设备模型和 自定义日志模型

    class Device(models.Model):
        """
        IT资产设备,主要是电脑和显示器
        """
        pass
    
    
    class CustomLogEntry(models.Model):
        """日志模型,记录其他模型上的变化记录"""
        user = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True)
        action_time = models.DateTimeField(auto_now_add=True)
        model_name = models.CharField(max_length=100)
        model_pk = models.CharField(max_length=100)
        action = models.CharField(max_length=10)
        data = models.TextField()
    
        def __str__(self):
            return f"{self.user} {self.model_name} {self.action}"
    
    

    2、定义信号

    from django.contrib.auth.models import User
    from django.contrib.admin.models import LogEntry
    from django.db.models.signals import post_save
    from django.dispatch import receiver
    
    # 这里只记录Device模型的变化
    # @receiver(post_save, sender=Device)
    @receiver(post_save)
    def log_save(sender, instance, created, **kwargs):
        print("sender: ", sender)
        action = 'CREATE' if created else 'UPDATE'
        print("current request in kwargs: ", kwargs)
        # current request in kwargs:  {'signal': <django.db.models.signals.ModelSignal object at 0x10b73d490>, 'update_fields': None, 'raw': False, 'using': 'default'}
        
        request = kwargs.get('requqest')
        print("instance: ", instance)
        print("current request in kwargs: ", request)
    

    3、Device模型注册到admin后台,然后创建超级管理员账号,登录后台对 Device模型进行新增、修改的操作

    过程日志输出如下

    sender:  <class 'demoapp.models.Device'>
    current request in kwargs:  {'signal': <django.db.models.signals.ModelSignal object at 0x103ec9730>, 'update_fields': None, 'raw': False, 'using': 'default'}
    instance:  华为 x14 (2)
    current request in kwargs:  None
    sender:  <class 'django.contrib.admin.models.LogEntry'>
    current request in kwargs:  {'signal': <django.db.models.signals.ModelSignal object at 0x103ec9730>, 'update_fields': None, 'raw': False, 'using': 'default'}
    instance:  Changed “华为 x14 (2)” — Changed 状态.
    current request in kwargs:  None
    

    从上面的日志分析

    3.1、从上面的log_save信号函数中知道,除了 sender/instance/created 之外的参数都在 kwargs 中

    3.2、输出kwargs尝试获取request 我们发现是request是None,所以Django的信号中是没有request的参数的,那么就无法通过request来获取当前登录的用户

    3.3、同时我们发现在未明确指定sender的情况,除了我们明确操作的Device模型之外,还多出来个 django.contrib.admin.models.LogEntry

    3.4、查看 django_admin_log 表中的数据发现,Django默认是帮忙记录了各个模型的变化(可以尝试新增用户、修改用户,发现变更操作也记录到了 django_admin_log 表中去)

    这里主要是研究自定义logEntry的情况下如果通过signal信号获取当前登录用户,关于 django_admin_log 记录的逻辑,我们下篇文章介绍

    问题修复

    查找了相关文档之后发现,django-threadlocals 模块可以很好的帮我们解决该问题

    1、安装第三方模块

    pip install django-threadlocals
    

    2、配置对应的中间件

    MIDDLEWARE = [
        # ... Other midddleware
        'threadlocals.middleware.ThreadLocalMiddleware',
    ]
    
    3、在然后使用 threadlocals中的 `get_current_request` 
    
    可以获取Django 后台的request,这个request中包括当前登录的user 
    
    ```python
    from django.db.models.signals import post_save
    from django.contrib.auth import get_user_model
    from django.contrib.auth.models import AnoymousUser
    from django.dispatch import receiver
    
    from threadlocals.threadlocals import get_current_request
    
    
    @receiver(post_save, sender=Device)
    def log_save(sender, instance, created, **kwargs):
        if sender in [SysUser, Device]:
            request = get_current_request()
            if request:
                user = request.user
            else:
                user = AnonymousUser()
            action = 'CREATE' if created else 'UPDATE'
            data = str(instance.__dict__)
            CustomLogEntry.objects.create(user=user, model_name=sender.__name__, model_pk=instance.pk, action=action, data=data)
    

    然后操作 Device的数据变更,然后查询 CustomLogEntry 记录,发现获取到了当前登录用户

    image.png

    相关文章

      网友评论

        本文标题:Django自带的Admin后台中如何获取当前登录用户

        本文链接:https://www.haomeiwen.com/subject/pjpgddtx.html